<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/%E6%AD%A6%E6%B1%8932x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/%E6%AD%A6%E6%B1%8916x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib1/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib1/pace/pace-theme-center-simple.min.css">
  <script src="/lib1/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="observable相关observable用法:  observable(value) @observable classProperty &#x3D; value  Observable 值可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射。 匹配类型应用了以下转换规则，但可以通过使用调节器进行微调。请参见下文。  如果 value 是ES6的 Map : 会返回一个新的 Observabl">
<meta property="og:type" content="article">
<meta property="og:title" content="Mobx进阶">
<meta property="og:url" content="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx%E8%BF%9B%E9%98%B6/index.html">
<meta property="og:site_name" content="Technological Blog">
<meta property="og:description" content="observable相关observable用法:  observable(value) @observable classProperty &#x3D; value  Observable 值可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射。 匹配类型应用了以下转换规则，但可以通过使用调节器进行微调。请参见下文。  如果 value 是ES6的 Map : 会返回一个新的 Observabl">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://example.com/image/Mobx%E8%BF%9B%E9%98%B6/observed-refs.png">
<meta property="article:published_time" content="2021-11-30T04:18:39.000Z">
<meta property="article:modified_time" content="2021-11-30T08:45:56.026Z">
<meta property="article:author" content="Li Yudong">
<meta property="article:tag" content="公司技术栈">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://example.com/image/Mobx%E8%BF%9B%E9%98%B6/observed-refs.png">

<link rel="canonical" href="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx%E8%BF%9B%E9%98%B6/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Mobx进阶 | Technological Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Technological Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">IT小白的成长之旅</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">18</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">14</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">95</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/tiarmor1" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx%E8%BF%9B%E9%98%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Mobx进阶
        </h1>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              
    
              <time title="创建时间：2021-11-30 12:18:39 / 修改时间：16:45:56" itemprop="dateCreated datePublished" datetime="2021-11-30T12:18:39+08:00">2021-11-30</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88/" itemprop="url" rel="index"><span itemprop="name">盒马技术栈</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
        <h1 id="observable相关"><a href="#observable相关" class="headerlink" title="observable相关"></a>observable相关</h1><h2 id="observable"><a href="#observable" class="headerlink" title="observable"></a>observable</h2><p>用法:</p>
<ul>
<li><code>observable(value)</code></li>
<li><code>@observable classProperty = value</code></li>
</ul>
<p>Observable 值可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射。 匹配类型应用了以下转换规则，但可以通过使用<strong>调节器</strong>进行微调。请参见下文。</p>
<ol>
<li>如果 <strong>value</strong> 是ES6的 <code>Map</code> : 会返回一个新的 <a href="https://cn.mobx.js.org/refguide/map.html" target="_blank" rel="noopener">Observable Map</a>。如果你不只关注某个特定entry的更改，而且对添加或删除其他entry时也做出反应的话，那么 Observable maps 会非常有用</li>
<li>如果 <strong>value</strong> 是数组，会返回一个 <a href="https://cn.mobx.js.org/refguide/array.html" target="_blank" rel="noopener">Observable Array</a>。</li>
<li>如果 <strong>value</strong> 是没有原型的对象，那么对象会被克隆并且所有的属性都会被转换成可观察的。参见 <a href="https://cn.mobx.js.org/refguide/object.html" target="_blank" rel="noopener">Observable Object</a>。</li>
<li>如果 <strong>value</strong> 是有原型的对象，JavaSript 原始数据类型或者函数，会返回一个 <a href="https://cn.mobx.js.org/refguide/boxed.html" target="_blank" rel="noopener">Boxed Observable</a>。MobX 不会将一个有原型的对象自动转换成可观察的，因为这是它构造函数的职责。在构造函数中使用 <code>extendObservable</code> 或者在类定义中使用 <code>@observable</code>。</li>
<li>如果 <strong>value</strong> 是有原型的对象，JavaSript 原始数据类型或者函数，<code>observable</code> 会抛出。如果想要为这样的值创建一个独立的可观察引用，请使用 <a href="https://cn.mobx.js.org/refguide/boxed.html" target="_blank" rel="noopener">Boxed Observable</a> observable 代替。MobX 不会将一个有原型的对象自动转换成可观察的，因为这是它构造函数的职责。在构造函数中使用 <code>extendObservable</code> 或在类定义上使用 <code>@observable</code> / <code>decorate</code> 。</li>
</ol>
<p>乍看之下，这些规则可能看上去很复杂，但实际上实践当中你会发现他们是非常直观的。</p>
<p>一些建议:</p>
<ul>
<li>要想使用 <code>@observable</code> 装饰器，首先要确保在你的编译器(babel 或者 typescript)中 <a href="http://mobxjs.github.io/mobx/refguide/observable-decorator.html" target="_blank" rel="noopener">装饰器是启用的</a>。</li>
<li>默认情况下将一个数据结构转换成可观察的是<strong>有感染性的</strong>，这意味着 <code>observable</code> 被自动应用于数据结构包含的任何值，或者将来会被该数据结构包含的值。这个行为可以通过使用 <em>modifiers</em> 来更改。</li>
<li><em>[MobX 4 及以下版本]</em> 要创建 <strong>键是动态的对象</strong> 时使用 <a href="https://cn.mobx.js.org/refguide/map.html" target="_blank" rel="noopener">Observable Map</a>！对象上只有初始化时便存在的属性会转换成可观察的，尽管新添加的属性可以通过使用 extendObservable 转换成可观察的。</li>
</ul>
<p>一些示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = observable.map(&#123; <span class="attr">key</span>: <span class="string">"value"</span>&#125;);</span><br><span class="line">map.set(<span class="string">"key"</span>, <span class="string">"new value"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> list = observable([<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>]);</span><br><span class="line">list[<span class="number">2</span>] = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> person = observable(&#123;</span><br><span class="line">    firstName: <span class="string">"Clive Staples"</span>,</span><br><span class="line">    lastName: <span class="string">"Lewis"</span></span><br><span class="line">&#125;);</span><br><span class="line">person.firstName = <span class="string">"C.S."</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> temperature = observable.box(<span class="number">20</span>);</span><br><span class="line">temperature.set(<span class="number">25</span>);</span><br></pre></td></tr></table></figure>

<h2 id="observable-1"><a href="#observable-1" class="headerlink" title="@observable"></a>@observable</h2><p>装饰器可以在 ES7 或者 TypeScript 类属性中属性使用，将其转换成可观察的。 <code>@observable</code> 可以在实例字段和属性 getter 上使用。 对于对象的哪部分需要成为可观察的，@observable 提供了细粒度的控制。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; observable, computed &#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OrderLine</span> </span>&#123;</span><br><span class="line">    @observable price = <span class="number">0</span>;</span><br><span class="line">    @observable amount = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    @computed <span class="keyword">get</span> total() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.amount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果你的环境不支持装饰器或字段初始化器，使用 <code>decorate</code> 来代替 (想了解更多，参见 <a href="https://cn.mobx.js.org/refguide/modifiers.html" target="_blank" rel="noopener">装饰</a>)。</p>
<h2 id="Observable-对象"><a href="#Observable-对象" class="headerlink" title="Observable 对象"></a>Observable 对象</h2><p>Usage <code>observable.object(props, decorators?, options?)</code></p>
<p>如果把一个普通的 JavaScript 对象传递给 <code>observable</code> 方法，对象的所有属性都将被拷贝至一个克隆对象并将克隆对象转变成可观察的。 (普通对象是指不是使用构造函数创建出来的对象，而是以 <code>Object</code> 作为其原型，或者根本没有原型。) 默认情况下，<code>observable</code> 是递归应用的，所以如果对象的某个值是一个对象或数组，那么该值也将通过 <code>observable</code> 传递。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observable, autorun, action&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = observable(&#123;</span><br><span class="line">    <span class="comment">// observable 属性:</span></span><br><span class="line">    name: <span class="string">"John"</span>,</span><br><span class="line">    age: <span class="number">42</span>,</span><br><span class="line">    showAge: <span class="literal">false</span>,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算属性:</span></span><br><span class="line">    <span class="keyword">get</span> labelText() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.showAge ? <span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span> (age: <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>)`</span> : <span class="keyword">this</span>.name;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 动作:</span></span><br><span class="line">    setAge(age) &#123;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    setAge: action</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 对象属性没有暴露 'observe' 方法,</span></span><br><span class="line"><span class="comment">// 但不用担心, 'mobx.autorun' 功能更加强大</span></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(person.labelText));</span><br><span class="line"></span><br><span class="line">person.name = <span class="string">"Dave"</span>;</span><br><span class="line"><span class="comment">// 输出: 'Dave'</span></span><br><span class="line"></span><br><span class="line">person.setAge(<span class="number">21</span>);</span><br><span class="line"><span class="comment">// 等等</span></span><br></pre></td></tr></table></figure>

<p>当使对象转变成 observable 时，需要记住一些事情:</p>
<ul>
<li>只有普通的对象可以转变成 observable 。对于非普通对象，构造函数负责初始化 observable 属性。 要么使用 <a href="https://cn.mobx.js.org/refguide/observable.html" target="_blank" rel="noopener"><code>@observable</code></a> 注解，要么使用 <a href="https://cn.mobx.js.org/refguide/extend-observable.html" target="_blank" rel="noopener"><code>extendObservable</code></a> 函数。</li>
<li>属性的 getter 会自动转变成衍生属性，就像 <a href="https://cn.mobx.js.org/refguide/computed-decorator" target="_blank" rel="noopener"><code>@computed</code></a> 所做的。</li>
<li><code>observable</code> 是自动递归到整个对象的。在实例化过程中和将来分配给 observable 属性的任何新值的时候。Observable 不会递归到非普通对象中。</li>
<li>这些默认行为能应对95%的场景，但想要更细粒度的控制，比如哪些属性应该转变成可观察的和如何变成可观察的，请参见<a href="https://cn.mobx.js.org/refguide/modifiers.html" target="_blank" rel="noopener">装饰器</a>。</li>
<li>传入 <code>{ deep: false }</code> 作为第三个参数可以禁用属性值的自动转换</li>
<li>传入 <code>{ name: &quot;my object&quot; }</code> 为本对象赋予友好的调试名称</li>
<li><em>[MobX 4 及以下版本]</em> 当通过 <code>observable</code> 传递对象时，只有在把对象转变 observable 时存在的属性才会是可观察的。 稍后添加到对象的属性不会变为可观察的，除非使用 <a href="https://cn.mobx.js.org/refguide/object-api.html" target="_blank" rel="noopener"><code>set</code></a> 或 <a href="https://cn.mobx.js.org/refguide/extend-observable.html" target="_blank" rel="noopener"><code>extendObservable</code></a>。</li>
</ul>
<h2 id="Observable-数组"><a href="#Observable-数组" class="headerlink" title="Observable 数组"></a>Observable 数组</h2><p>和对象类似，可以使用 <code>observable.array(values?)</code> 或者将数组传给 <code>observable</code>，可以将数组转变为可观察的。 这也是递归的，所以数组中的所有(未来的)值都会是可观察的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observable, autorun&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> todos = observable([</span><br><span class="line">    &#123; <span class="attr">title</span>: <span class="string">"Spoil tea"</span>, <span class="attr">completed</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">    &#123; <span class="attr">title</span>: <span class="string">"Make coffee"</span>, <span class="attr">completed</span>: <span class="literal">false</span> &#125;</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Remaining:"</span>, todos</span><br><span class="line">        .filter(<span class="function"><span class="params">todo</span> =&gt;</span> !todo.completed)</span><br><span class="line">        .map(<span class="function"><span class="params">todo</span> =&gt;</span> todo.title)</span><br><span class="line">        .join(<span class="string">", "</span>)</span><br><span class="line">    );</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 输出: 'Remaining: Make coffee'</span></span><br><span class="line"></span><br><span class="line">todos[<span class="number">0</span>].completed = <span class="literal">false</span>;</span><br><span class="line"><span class="comment">// 输出: 'Remaining: Spoil tea, Make coffee'</span></span><br><span class="line"></span><br><span class="line">todos[<span class="number">2</span>] = &#123; <span class="attr">title</span>: <span class="string">'Take a nap'</span>, <span class="attr">completed</span>: <span class="literal">false</span> &#125;;</span><br><span class="line"><span class="comment">// 输出: 'Remaining: Spoil tea, Make coffee, Take a nap'</span></span><br><span class="line"></span><br><span class="line">todos.shift();</span><br><span class="line"><span class="comment">// 输出: 'Remaining: Make coffee, Take a nap'</span></span><br></pre></td></tr></table></figure>

<p>除了所有内置函数，observable 数组还可以使用下面的好东西:</p>
<ul>
<li><code>intercept(interceptor)</code> - 可以用来在任何变化作用于数组前将其拦截。参见 <a href="https://cn.mobx.js.org/refguide/observe.html" target="_blank" rel="noopener">observe &amp; intercept</a></li>
<li><code>observe(listener, fireImmediately? = false)</code> - 监听数组的变化。回调函数将接收表示数组拼接或数组更改的参数，它符合 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/observe" target="_blank" rel="noopener">ES7 提议</a>。它返回一个清理函数以用来停止监听器。</li>
<li><code>clear()</code> - 从数组中删除所有项。</li>
<li><code>replace(newItems)</code> - 用新项替换数组中所有已存在的项。</li>
<li><code>find(predicate: (item, index, array) =&gt; boolean, thisArg?)</code> - 基本上等同于 ES7 的 <code>Array.find</code> 提议。</li>
<li><code>findIndex(predicate: (item, index, array) =&gt; boolean, thisArg?)</code> - 基本上等同于 ES7 的 <code>Array.findIndex</code> 提议。</li>
<li><code>remove(value)</code> - 通过值从数组中移除一个单个的项。如果项被找到并移除的话，返回 <code>true</code> 。</li>
<li><em>[MobX 4 及以下版本]</em> <code>peek()</code> - 和 <code>slice()</code> 类似， 返回一个有所有值的数组并且数组可以放心的传递给其它库。</li>
</ul>
<p>不同于 <code>sort</code> 和 <code>reverse</code> 函数的内置实现，observableArray.sort 和 observableArray.reverse 不会改变数组本身，而只是返回一个排序过/反转过的拷贝。在 MobX 5 及以上版本中会出现警告。推荐使用 <code>array.slice().sort()</code> 来替代。</p>
<p><code>observable.array(values, { deep: false })</code></p>
<p>任何分配给 observable 数组的值都会默认通过 <a href="https://cn.mobx.js.org/refguide/observable.html" target="_blank" rel="noopener"><code>observable</code></a> 来使其转变成可观察的。 创建浅数组以禁用此行为，并按原样存储值。关于此机制的更多详情，请参见 <a href="https://cn.mobx.js.org/refguide/modifiers.html" target="_blank" rel="noopener">调节器</a>。</p>
<p><code>observable.array(values, { name: &quot;my array&quot; })</code></p>
<p><code>name</code> 选项用来给数组一个友好的调试名称，用于 <code>spy</code> 或者 MobX 开发者工具。</p>
<p>MobX 4 及以下版本中数组的局限性：</p>
<p>由于 ES5 中的原生数组的局限性，<code>observable.array</code> 会创建一个人造数组(类数组对象)来代替真正的数组。 实际上，这些数组能像原生数组一样很好的工作，并且支持所有的原生方法，包括从索引的分配到包含数组长度。</p>
<p>请记住无论如何 <code>Array.isArray(observable([]))</code> 都将返回 <code>false</code> ，所以无论何时当你需要传递 observable 数组到外部库时，通过使用 <code>array.slice()</code> <strong>在 observable 数组传递给外部库或者内置方法前创建一份浅拷贝</strong>(无论如何这都是最佳实践)总会是一个好主意。 换句话说，<code>Array.isArray(observable([]).slice())</code> 会返回 <code>true</code>。</p>
<h2 id="Observable-映射"><a href="#Observable-映射" class="headerlink" title="Observable 映射"></a>Observable 映射</h2><p><code>observable.map(values, options?)</code>：</p>
<p><code>observable.map(values?)</code>： 创建一个动态键的 observable 映射。 如果你不但想对一个特定项的更改做出反应，而且对添加或删除该项也做出反应的话，那么 observable 映射会非常有用。 <code>observable.map(values)</code> 中的 values 可以是对象、 数组或者字符串键的 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map" target="_blank" rel="noopener">ES6 map</a>。</p>
<p>通过 ES6 Map 构造函数，你可以使用 <code>observable(new Map())</code> 或使用装饰器 <code>@observable mavvvvgggggg    p  = new Map()</code> 的类属性来初始化 observable 映射 。</p>
<p>下列 observable 映射所暴露的方法是依据 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map" target="_blank" rel="noopener">ES6 Map 规范</a>:</p>
<ul>
<li><code>has(key)</code> - 返回映射是否有提供键对应的项。注意键的存在本身就是可观察的。</li>
<li><code>set(key, value)</code> - 把给定键的值设置为 <code>value</code> 。提供的键如果在映射中不存在的话，那么它会被添加到映射之中。</li>
<li><code>delete(key)</code> - 把给定键和它的值从映射中删除。</li>
<li><code>get(key)</code> - 返回给定键的值(或 <code>undefined</code>)。</li>
<li><code>keys()</code> - 返回映射中存在的所有键的迭代器。插入顺序会被保留。</li>
<li><code>values()</code> - 返回映射中存在的所有值的迭代器。插入顺序会被保留。</li>
<li><code>entries()</code> - 返回一个(保留插入顺序)的数组的迭代器，映射中的每个键值对都会对应数组中的一项 <code>[key, value]</code>。</li>
<li><code>forEach(callback:(value, key, map) =&gt; void, thisArg?)</code> - 为映射中每个键值对调用给定的回调函数。</li>
<li><code>clear()</code> - 移除映射中的所有项。</li>
<li><code>size</code> - 返回映射中项的数量。</li>
</ul>
<p>以下函数不属于 ES6 规范，而是由 MobX 提供:</p>
<ul>
<li><code>toJS()</code> - 将 observable 映射转换成普通映射。</li>
<li><code>toJSON()</code>. 返回此映射的浅式普通对象表示。(想要深拷贝，请使用 <code>mobx.toJS(map)</code>)。</li>
<li><code>intercept(interceptor)</code> - 可以用来在任何变化作用于映射前将其拦截。参见 <a href="https://cn.mobx.js.org/refguide/observe.html" target="_blank" rel="noopener">observe &amp; intercept</a>。</li>
<li><code>observe(listener, fireImmediately?)</code> - 注册侦听器，在映射中的每个更改时触发，类似于为 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe" target="_blank" rel="noopener">Object.observe</a> 发出的事件。想了解更多详情，请参见 <a href="https://cn.mobx.js.org/refguide/observe.html" target="_blank" rel="noopener">observe &amp; intercept</a>。</li>
<li><code>merge(values)</code> - 把提供对象的所有项拷贝到映射中。<code>values</code> 可以是普通对象、entries 数组或者 ES6 字符串键的映射。</li>
<li><code>replace(values)</code> - 用提供值替换映射全部内容。是 <code>.clear().merge(values)</code> 的简写形式。</li>
</ul>
<p><code>observable.map(values, { deep: false })</code>：</p>
<p>任何分配给 observable 映射的值都会默认通过 <a href="https://cn.mobx.js.org/refguide/observable.html" target="_blank" rel="noopener"><code>observable</code></a> 来传递使其转变成可观察的。 创建浅映射以禁用此行为，并按原样存储值。关于此机制的更多详情，请参见 <a href="https://cn.mobx.js.org/refguide/modifiers.html" target="_blank" rel="noopener">装饰器</a>。</p>
<p><code>observable.map(values, { name: &quot;my map&quot; })</code>：</p>
<p><code>name</code> 选项用来给数组一个友好的调试名称，用于 <code>spy</code> 或者 MobX 开发者工具。</p>
<h2 id="原值类型值和引用类型值"><a href="#原值类型值和引用类型值" class="headerlink" title="原值类型值和引用类型值"></a>原值类型值和引用类型值</h2><p>JavaScript 中的所有原始类型值都是不可变的，因此它们都是不可观察的。 通常这是好的，因为 MobX 通常可以使包含值的<strong>属性</strong>转变成可观察的。 可参见 <a href="https://cn.mobx.js.org/refguide/object.html" target="_blank" rel="noopener">observable objects</a>。 在极少数情况下，拥有一个不属于某个对象的可观察的“原始类型值”还是很方便的。 对于这种情况，可以创建一个 observable box 以便管理这样的原始类型值。</p>
<p><code>observable.box(value)</code>：</p>
<p><code>observable.box(value)</code> 接收任何值并把值存储到箱子中。 使用 <code>.get()</code> 可以获取当前值，使用 <code>.set(newValue)</code> 可以更新值。</p>
<p>此外，还可以使用它的 <code>.observe</code> 方法注册回调，以监听对存储值的更改。 但因为 MobX 自动追踪了箱子的变化，在绝大多数情况下最好还是使用像 <a href="https://cn.mobx.js.org/refguide/autorun.html" target="_blank" rel="noopener"><code>mobx.autorun</code></a> 这样的 reaction 来替代。</p>
<p><code>observable.box(scalar)</code> 返回的对象签名是:</p>
<ul>
<li><code>.get()</code> - 返回当前值。</li>
<li><code>.set(value)</code> - 替换当前存储的值并通知所有观察者。</li>
<li><code>intercept(interceptor)</code> - 可以用来在任何变化应用前将其拦截。参见 <a href="https://cn.mobx.js.org/refguide/observe.html" target="_blank" rel="noopener">observe &amp; intercept</a>。</li>
<li><code>.observe(callback: (change) =&gt; void, fireImmediately = false): disposerFunction</code> - 注册一个观察者函数，每次存储值被替换时触发。返回一个函数以取消观察者。参见 <a href="https://cn.mobx.js.org/refguide/observe.html" target="_blank" rel="noopener">observe &amp; intercept</a>。<code>change</code> 参数是一个对象，其中包含 observable 的 <code>newValue</code> 和 <code>oldValue</code> 。</li>
</ul>
<p><code>observable.box(value, { deep: false })</code>：</p>
<p>创建一个基于 <a href="https://cn.mobx.js.org/refguide/modifiers.html" target="_blank" rel="noopener"><code>ref</code></a> 装饰器的箱子。这意味着箱子里的任何(将来)值都不会自动地转换成 observable 。</p>
<p>示例：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observable&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cityName = observable.box(<span class="string">"Vienna"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cityName.get());</span><br><span class="line"><span class="comment">// 输出 'Vienna'</span></span><br><span class="line"></span><br><span class="line">cityName.observe(<span class="function"><span class="keyword">function</span>(<span class="params">change</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(change.oldValue, <span class="string">"-&gt;"</span>, change.newValue);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">cityName.set(<span class="string">"Amsterdam"</span>);</span><br><span class="line"><span class="comment">// 输出 'Vienna -&gt; Amsterdam'</span></span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><code>observable.box(value, { name: &quot;my array&quot; })</code>：</p>
<p><code>name</code> 选项用来给数组一个友好的调试名称，用于 <code>spy</code> 或者 MobX 开发者工具。</p>
<h2 id="装饰器"><a href="#装饰器" class="headerlink" title="装饰器"></a>装饰器</h2><p>MobX 有一组装饰器来定义 observable 属性的行为。</p>
<ul>
<li><code>observable</code>: <code>observable.deep</code> 的别名</li>
<li><code>observable.deep</code>: 任何 observable 都使用的默认的调节器。它将任何(尚未成为 observable )数组，映射或纯对象克隆并转换为 observable 对象，并将其赋值给给定属性</li>
<li><code>observable.ref</code>: 禁用自动的 observable 转换，只是创建一个 observable 引用</li>
<li><code>observable.shallow</code>: 只能与集合组合使用。 将任何分配的集合转换为 observable，但该集合的值将按原样处理</li>
<li><code>observable.struct</code>: 就像 <code>ref</code>, 但会忽略结构上等于当前值的新值</li>
<li><code>computed</code>: 创建一个衍生属性, 参见 <a href="https://cn.mobx.js.org/refguide/computed-decorator.html" target="_blank" rel="noopener"><code>computed</code></a></li>
<li><code>computed(options)</code>: 同 computed , 可设置选项</li>
<li><code>computed.struct</code>: 与 <code>computed</code> 相同，但是只有当视图产生的值与之前的值结构上有不同时，才通知它的观察者</li>
<li><code>action</code>: 创建一个动作, 参见 <a href="https://cn.mobx.js.org/refguide/action.html" target="_blank" rel="noopener"><code>action</code></a></li>
<li><code>action(name)</code>: 创建一个动作，重载了名称</li>
<li><code>action.bound</code>: 创建一个动作, 并将 <code>this</code> 绑定到了实例</li>
</ul>
<p>装饰器可以使用 API <code>decorate</code>、<code>observable.object</code>、<code>extendObservable</code> 和 <code>observable</code> (创建对象时) 来指定对象成员的行为。 如果没有传入装饰器，默认为对任意键值对使用 <code>observable.deep</code>，对 getters 使用 <code>computed</code> 。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observable, autorun, action&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = observable(&#123;</span><br><span class="line">    name: <span class="string">"John"</span>,</span><br><span class="line">    age: <span class="number">42</span>,</span><br><span class="line">    showAge: <span class="literal">false</span>,</span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> labelText() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.showAge ? <span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span> (age: <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>)`</span> : <span class="keyword">this</span>.name;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 动作:</span></span><br><span class="line">    setAge(age) &#123;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    setAge: action</span><br><span class="line">    <span class="comment">// 其他属性默认为 observables / computed</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    name = <span class="string">"John"</span></span><br><span class="line">    age = <span class="number">42</span></span><br><span class="line">    showAge = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> labelText() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.showAge ? <span class="string">`<span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span> (age: <span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>)`</span> : <span class="keyword">this</span>.name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    setAge(age) &#123;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 使用 decorate 时，所有字段都应该指定 (毕竟，类里的非 observable 字段可能会更多)</span></span><br><span class="line">decorate(Person, &#123;</span><br><span class="line">    name: observable,</span><br><span class="line">    age: observable,</span><br><span class="line">    showAge: observable,</span><br><span class="line">    labelText: computed,</span><br><span class="line">    setAge: action</span><br><span class="line">&#125;)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><strong>深层可观察性：</strong></p>
<p>当 MobX 创建一个 observable 对象时，(使用 <code>observable</code>、 <code>observable.object</code> 或 <code>extendObservable</code>)，它引入的 observable 属性默认是使用 <code>deep</code> 调节器的。<code>deep</code> 调节器主要是为任何新分配的值递归调用 <code>observable(newValue)</code>。 会依次使用 <code>deep</code> 调节器…你可以想象。</p>
<p>这是一个非常便利的默认设置。无需额外的工作，分配给 observable 的所有值本身也将转变成 observable(除非它们已经是)，因此不需要额外的工作就可使对象转变成深 observable 。</p>
<p><strong>引用可观察性：</strong></p>
<p>然后在某些情况下，不需要将对象转变成 observable 。 典型案例就是不可变对象，或者不是由你管理，而是由外部库管理的对象。 例如 JSX 元素、DOM 元素、像 History、window 这样的原生对象，等等。 对于这类对象，只需要存储引用而不用把它们转变成 observable 。</p>
<p>对于这些情况，可以使用 <code>ref</code> 调节器。它会确保创建 observable 属性时，只追踪引用而不会把它的值转变成 observable 。 示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Message</span> </span>&#123;</span><br><span class="line">    @observable message = <span class="string">"Hello world"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 虚构的例子，如果 author 是不可变的，我们只需要存储一个引用，不应该把它变成一个可变的 observable 对象</span></span><br><span class="line">    @observable.ref author = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>或者使用 ES5 语法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Message</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    extendObservable(&#123;</span><br><span class="line">        message: <span class="string">"Hello world"</span>,</span><br><span class="line">        author: <span class="literal">null</span></span><br><span class="line">    &#125;, &#123;</span><br><span class="line">        author: observable.ref</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>注意，可以通过使用 <code>const box = observable.shallowBox（value）</code> 来创建一个装箱的 observable 引用</p>
<p><strong>浅层可观察性：</strong></p>
<p><code>observable.shallow</code> 调节器会应用“单层”可观察性。如果想创建一个 observable 引用的<strong>集合</strong>，那你会需要它。 如果新集合分配给具有此调节器的属性，那么它会转变成 observable，但它的值将保持原样，不同于 <code>deep</code> 的是它不会递归。 示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AuthorStore</span> </span>&#123;</span><br><span class="line">    @observable.shallow authors = []</span><br><span class="line">&#125;</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>在上面的示例中，使用普通的 author 数组分配给 <code>authors</code> 的话，会使用 observables 数组来更新 author，observables 数组包含原始的、非 observable 的 author 。</p>
<p>注意， <code>{ deep: false }</code> 了作为选项传给 <code>observable</code>、<code>observable.object</code>、<code>observable.array</code>、<code>observable.map</code> 和 <code>extendObservable</code> 来创建浅集合。</p>
<h1 id="对-observables-作出响应"><a href="#对-observables-作出响应" class="headerlink" title="对 observables 作出响应"></a>对 observables 作出响应</h1><h2 id="computed"><a href="#computed" class="headerlink" title="(@)computed"></a>(@)computed</h2><p>计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。 概念上来说，它们与excel表格中的公式十分相似。 不要低估计算值，因为它们有助于使实际可修改的状态尽可能的小。 此外计算值还是高度优化过的，所以尽可能的多使用它们。</p>
<p>不要把 <code>computed</code> 和 <code>autorun</code> 搞混。它们都是响应式调用的表达式，但是，如果你想响应式的产生一个可以被其它 observer 使用的<strong>值</strong>，请使用 <code>@computed</code>，如果你不想产生一个新值，而想要达到一个<strong>效果</strong>，请使用 <code>autorun</code>。 举例来说，效果是像打印日志、发起网络请求等这样命令式的副作用。</p>
<p>如果任何影响计算值的值发生变化了，计算值将根据状态自动进行衍生。 <strong>计算值在大多数情况下可以被 MobX 优化的，因为它们被认为是纯函数。</strong> 例如，如果前一个计算中使用的数据没有更改，计算属性将不会重新运行。 如果某个其它计算属性或 reaction 未使用该计算属性，也不会重新运行。 在这种情况下，它将被暂停。</p>
<p>这个自动地暂停是非常方便的。如果一个计算值不再被观察了，例如使用它的UI不复存在了，MobX 可以自动地将其垃圾回收。而 <strong><code>autorun</code> 中的值必须要手动清理才行，</strong>这点和计算值是有所不同的。如果你创建一个计算属性，但不在 reaction 中的任何地方使用它，它不会缓存值并且有些重新计算看起来似乎是没有必要的。这点有时候会让刚接触 MobX 的人们很困惑。 然而，在现实开发场景中，这是迄今为止最好的默认逻辑。如果你需要的话，可以使用 <a href="https://cn.mobx.js.org/refguide/observe.html" target="_blank" rel="noopener"><code>observe</code></a> 或 <a href="https://github.com/mobxjs/mobx-utils#keepalive" target="_blank" rel="noopener"><code>keepAlive</code></a> 来强制保持计算值总是处于唤醒状态。</p>
<p>注意计算属性是不可枚举的，它们也不能在继承链中被覆盖。 </p>
<p><code>@computed</code>:如果已经<a href="https://cn.mobx.js.org/best/decorators.html" target="_blank" rel="noopener">启用 decorators</a> 的话，可以在任意类属性的 getter 上使用 <code>@computed</code> 装饰器来声明式的创建计算属性。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observable, computed&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OrderLine</span> </span>&#123;</span><br><span class="line">    @observable price = <span class="number">0</span>;</span><br><span class="line">    @observable amount = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(price) &#123;</span><br><span class="line">        <span class="keyword">this</span>.price = price;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @computed <span class="keyword">get</span> total() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.amount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>要不使用 <code>decorate</code> 来引入:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;decorate, observable, computed&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OrderLine</span> </span>&#123;</span><br><span class="line">    price = <span class="number">0</span>;</span><br><span class="line">    amount = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">constructor</span>(price) &#123;</span><br><span class="line">        <span class="keyword">this</span>.price = price;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> total() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.amount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">decorate(OrderLine, &#123;</span><br><span class="line">    price: observable,</span><br><span class="line">    amount: observable,</span><br><span class="line">    total: computed</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p><strong><code>observable.object</code> 和 <code>extendObservable</code> 都会自动将 getter 属性推导成计算属性</strong>，所以下面这样就足够了:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> orderLine = observable.object(&#123;</span><br><span class="line">    price: <span class="number">0</span>,</span><br><span class="line">    amount: <span class="number">1</span>,</span><br><span class="line">    <span class="keyword">get</span> total() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.amount</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p><strong>计算值的 setter</strong></p>
<p>还可以为计算值定义 setter。注意这些 setters 不能用来直接改变计算属性的值，但是它们可以用来作“逆向”衍生。例如:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> orderLine = observable.object(&#123;</span><br><span class="line">    price: <span class="number">0</span>,</span><br><span class="line">    amount: <span class="number">1</span>,</span><br><span class="line">    <span class="keyword">get</span> total() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.price * <span class="keyword">this</span>.amount</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="keyword">set</span> total(total) &#123;</span><br><span class="line">        <span class="keyword">this</span>.price = total / <span class="keyword">this</span>.amount <span class="comment">// 从 total 中推导出 price</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>同样的</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> </span>&#123;</span><br><span class="line">    @observable length = <span class="number">2</span>;</span><br><span class="line">    @computed <span class="keyword">get</span> squared() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.length * <span class="keyword">this</span>.length;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">set</span> squared(value) &#123; <span class="comment">// 这是一个自动的动作，不需要注解</span></span><br><span class="line">        <span class="keyword">this</span>.length = <span class="built_in">Math</span>.sqrt(value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><em>注意: 永远在 getter <strong>之后</strong> 定义 setter，一些 TypeScript 版本会知道声明了两个具有相同名称的属性。</em></p>
<p><strong><code>computed(expression)</code> 函数</strong></p>
<p><code>computed</code> 还可以直接当做函数来调用。 就像 <code>observable.box(primitive value)</code> 创建一个独立的 observable。 在返回的对象上使用 <code>.get()</code> 来获取计算的当前值，或者使用 <code>.observe(callback)</code> 来观察值的改变。这种形式的 <code>computed</code> 不常使用，但在某些情况下，你需要传递一个“在box中”的计算值时，它可能是有用的。示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observable, computed&#125; <span class="keyword">from</span> <span class="string">"mobx"</span>;</span><br><span class="line"><span class="keyword">var</span> name = observable.box(<span class="string">"John"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> upperCaseName = computed(<span class="function"><span class="params">()</span> =&gt;</span></span><br><span class="line">    name.get().toUpperCase()</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> disposer = upperCaseName.observe(<span class="function"><span class="params">change</span> =&gt;</span> <span class="built_in">console</span>.log(change.newValue));</span><br><span class="line"></span><br><span class="line">name.set(<span class="string">"Dave"</span>);</span><br><span class="line"><span class="comment">// 输出: 'DAVE'</span></span><br></pre></td></tr></table></figure>

<p><strong><code>computed</code> 的选项</strong></p>
<p>当使用 <code>computed</code> 作为调节器或者盒子，它接收的第二个选项参数对象，选项参数对象有如下可选参数:</p>
<ul>
<li><code>name</code>: 字符串, 在 spy 和 MobX 开发者工具中使用的调试名称</li>
<li><code>context</code>: 在提供的表达式中使用的 <code>this</code></li>
<li><code>set</code>: 要使用的setter函数。 没有 setter 的话无法为计算值分配新值。 如果传递给 <code>computed</code> 的第二个参数是一个函数，那么就把会这个函数作为 setter</li>
<li><code>equals</code>: 默认值是 <code>comparer.default</code> 。它充当比较前一个值和后一个值的比较函数。如果这个函数认为前一个值和后一个值是相等的，那么观察者就不会重新评估。这在使用结构数据和来自其他库的类型时很有用。例如，一个 computed 的 <a href="https://momentjs.com/" target="_blank" rel="noopener">moment</a> 实例可以使用 <code>(a, b) =&gt; a.isSame(b)</code> 。如果想要使用结构比较来确定新的值是否与上个值不同 (并作为结果通知观察者)，<code>comparer.deep</code> 十分便利。</li>
<li><code>requiresReaction</code>: 对于非常昂贵的计算值，推荐设置成 <code>true</code> 。如果你尝试读取它的值，但某些观察者没有跟踪该值（在这种情况下，MobX 不会缓存该值），则会导致计算结果丢失，而不是进行昂贵的重新评估。</li>
<li><code>keepAlive</code>: 如果没有任何人观察到，则不要使用此计算值。 <em>请注意，这很容易导致内存泄漏，因为它会导致此计算值使用的每个 observable ，并将计算值保存在内存中！</em></li>
</ul>
<p><strong><code>@computed.struct</code> 用于比较结构</strong></p>
<p><code>@computed</code> 装饰器不需要接收参数。如果你想创建一个能进行结构比较的计算属性时，请使用 <code>@computed.struct</code>。</p>
<p><strong>内置比较器</strong></p>
<p>MobX 提供了三个内置 <code>comparer</code> (比较器) ，它们应该能满足绝大部分需求：</p>
<ul>
<li><code>comparer.identity</code>: 使用恒等 (<code>===</code>) 运算符来判定两个值是否相同。</li>
<li><code>comparer.default</code>: 等同于 <code>comparer.identity</code>，但还认为 <code>NaN</code> 等于 <code>NaN</code> 。</li>
<li><code>comparer.structural</code>: 执行深层结构比较以确定两个值是否相同。</li>
</ul>
<p><strong>错误处理</strong></p>
<p>如果计算值在其计算期间抛出异常，则此异常将捕获并在读取其值时重新抛出。 强烈建议始终抛出“错误”，以便保留原始堆栈跟踪。 例如：<code>throw new Error（“Uhoh”）</code>, 而不是<code>throw &quot;Uhoh&quot;</code>。 抛出异常不会中断跟踪，所有计算值可以从异常中恢复。示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> x = observable.box(<span class="number">3</span>)</span><br><span class="line"><span class="keyword">const</span> y = observable.box(<span class="number">1</span>)</span><br><span class="line"><span class="keyword">const</span> divided = computed(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (y.get() === <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Division by zero"</span>)</span><br><span class="line">    <span class="keyword">return</span> x.get() / y.get()</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">divided.get() <span class="comment">// 返回 3</span></span><br><span class="line"></span><br><span class="line">y.set(<span class="number">0</span>) <span class="comment">// OK</span></span><br><span class="line">divided.get() <span class="comment">// 报错: Division by zero</span></span><br><span class="line">divided.get() <span class="comment">// 报错: Division by zero</span></span><br><span class="line"></span><br><span class="line">y.set(<span class="number">2</span>)</span><br><span class="line">divided.get() <span class="comment">// 已恢复; 返回 1.5</span></span><br></pre></td></tr></table></figure>

<h2 id="Autorun"><a href="#Autorun" class="headerlink" title="Autorun"></a>Autorun</h2><p>当你想创建一个响应式函数，而该函数本身永远不会有观察者时,可以使用 <code>mobx.autorun</code>。 这<strong>通常是当你需要从反应式代码桥接到命令式代码的情况</strong>，例如打印日志、持久化或者更新UI的代码。 <strong>当使用 <code>autorun</code> 时，所提供的函数总是立即被触发一次，然后每次它的依赖关系改变时会再次被触发。 相比之下，<code>computed(function)</code> 创建的函数只有当它有自己的观察者时才会重新计算，否则它的值会被认为是不相关的。</strong> 经验法则：<strong>如果你有一个函数应该自动运行，但不会产生一个新的值，请使用<code>autorun</code>。 其余情况都应该使用 <code>computed</code>。</strong> Autoruns 是关于 <em>启动效果</em> (initiating effects) 的 ，而不是产生新的值。 如果字符串作为第一个参数传递给 <code>autorun</code> ，它将被用作调试名。（感觉很像是vue中watch与computed的区别）</p>
<p>传递给 autorun 的函数在调用后将接收一个参数，即当前reaction(autorun)，可用于在执行期间清理 autorun。就像 [<code>@ observer</code> 装饰器/函数](<a href="https://cn.mobx.js.org/refguide/" target="_blank" rel="noopener">https://cn.mobx.js.org/refguide/</a> observer-component.md)，<code>autorun</code> 只会观察在执行提供的函数时所使用的数据。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> numbers = observable([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]);</span><br><span class="line"><span class="keyword">var</span> sum = computed(<span class="function"><span class="params">()</span> =&gt;</span> numbers.reduce(<span class="function">(<span class="params">a, b</span>) =&gt;</span> a + b, <span class="number">0</span>));</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> disposer = autorun(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(sum.get()));</span><br><span class="line"><span class="comment">// 输出 '6'</span></span><br><span class="line">numbers.push(<span class="number">4</span>);</span><br><span class="line"><span class="comment">// 输出 '10'</span></span><br><span class="line"></span><br><span class="line">disposer();</span><br><span class="line">numbers.push(<span class="number">5</span>);</span><br><span class="line"><span class="comment">// 不会再输出任何值。`sum` 不会再重新计算。</span></span><br></pre></td></tr></table></figure>

<p><strong>选项</strong>：</p>
<p>Autorun 接收第二个参数，它是一个参数对象，有如下可选的参数:</p>
<ul>
<li><code>delay</code>: 可用于对效果函数进行去抖动的数字(以毫秒为单位)。如果是 0(默认值) 的话，那么不会进行去抖。</li>
<li><code>name</code>: 字符串，用于在例如像 <a href="https://cn.mobx.js.org/refguide/spy.html" target="_blank" rel="noopener"><code>spy</code></a> 这样事件中用作此 reaction 的名称。</li>
<li><code>onError</code>: 用来处理 reaction 的错误，而不是传播它们。</li>
<li><code>scheduler</code>: 设置自定义调度器以决定如何调度 autorun 函数的重新运行</li>
</ul>
<p><strong><code>delay</code> 选项</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 假设 profile.asJson 返回的是 observable Json 表示，</span></span><br><span class="line">    <span class="comment">// 每次变化时将其发送给服务器，但发送前至少要等300毫秒。</span></span><br><span class="line">    <span class="comment">// 当发送后，profile.asJson 的最新值会被使用。</span></span><br><span class="line">    sendProfileToServer(profile.asJson);</span><br><span class="line">&#125;, &#123; <span class="attr">delay</span>: <span class="number">300</span> &#125;);</span><br></pre></td></tr></table></figure>

<p><strong><code>onError</code> 选项</strong></p>
<p>在 autorun 和所有其他类型 reaction 中抛出的异常会被捕获并打印到控制台，但不会将异常传播回原始导致异常的代码。 这是为了确保一个异常中的 reaction 不会阻止其他可能不相关的 reaction 的预定执行。 这也允许 reaction 从异常中恢复; 抛出异常不会破坏 MobX的跟踪，因此如果除去异常的原因，reaction 的后续运行可能会再次正常完成。</p>
<p>可以通过提供 <code>onError</code> 选项来覆盖 Reactions 的默认日志行为。 示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> age = observable.box(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> dispose = autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (age.get() &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Age should not be negative"</span>)</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"Age"</span>, age.get())</span><br><span class="line">&#125;, &#123;</span><br><span class="line">    onError(e) &#123;</span><br><span class="line">        <span class="built_in">window</span>.alert(<span class="string">"Please enter a valid age"</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>一个全局的 onError 处理方法可以使用 <code>onReactionError(handler)</code> 来设置。这在测试或监控中很有用。</p>
<h2 id="when"><a href="#when" class="headerlink" title="when"></a>when</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">when(predicate: <span class="function"><span class="params">()</span> =&gt;</span> boolean, effect?: <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">void</span>, options?)</span><br></pre></td></tr></table></figure>

<p><code>when</code> 观察并运行给定的 <code>predicate</code>，直到返回true。 一旦返回 true，给定的 <code>effect</code> 就会被执行，然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。</p>
<p>对于以响应式方式来进行处理或者取消，此函数非常有用。 示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyResource</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        when(</span><br><span class="line">            <span class="comment">// 一旦...</span></span><br><span class="line">            () =&gt; !<span class="keyword">this</span>.isVisible,</span><br><span class="line">            <span class="comment">// ... 然后</span></span><br><span class="line">            () =&gt; <span class="keyword">this</span>.dispose()</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @computed <span class="keyword">get</span> isVisible() &#123;</span><br><span class="line">        <span class="comment">// 标识此项是否可见</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    dispose() &#123;</span><br><span class="line">        <span class="comment">// 清理</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><strong>when-promise</strong></p>
<p>如果没提供 <code>effect</code> 函数，<code>when</code> 会返回一个 <code>Promise</code> 。它与 <code>async / await</code> 可以完美结合。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">await</span> when(<span class="function"><span class="params">()</span> =&gt;</span> that.isVisible)</span><br><span class="line">    <span class="comment">// 等等..</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="Reaction"><a href="#Reaction" class="headerlink" title="Reaction"></a>Reaction</h2><p>用法: <code>reaction(() =&gt; data, (data, reaction) =&gt; { sideEffect }, options?)</code>。</p>
<p><code>autorun</code> 的变种，对于如何追踪 observable 赋予了更细粒度的控制。 它接收两个函数参数，第一个(<em>数据</em> 函数)是用来追踪并返回数据作为第二个函数(<em>效果</em> 函数)的输入。 不同于 <code>autorun</code> 的是当创建时<em>效果</em> 函数不会直接运行，只有在数据表达式首次返回一个新值后才会运行。 在执行 <em>效果</em> 函数时访问的任何 observable 都不会被追踪。<code>reaction</code> 返回一个清理函数。</p>
<p>传入 <code>reaction</code> 的第二个函数(副作用函数)当调用时会接收两个参数。 第一个参数是由 <em>data</em> 函数返回的值。 第二个参数是当前的 reaction，可以用来在执行期间清理 <code>reaction</code></p>
<p>值得注意的是 <em>效果</em> 函数<strong>仅</strong>对数据函数中<strong>访问</strong>的数据作出反应，这可能会比实际在效果函数使用的数据要少。 此外，<em>效果</em> 函数只会在表达式返回的数据发生更改时触发。 换句话说: <code>reaction</code>需要你生产 <em>效果</em> 函数中所需要的东西。</p>
<p><strong>选项</strong>：</p>
<p>Reaction 接收第三个参数，它是一个参数对象，有如下可选的参数:</p>
<ul>
<li><code>fireImmediately</code>: 布尔值，用来标识效果函数是否在数据函数第一次运行后立即触发。默认值是 <code>false</code> 。</li>
<li><code>delay</code>: 可用于对效果函数进行去抖动的数字(以毫秒为单位)。如果是 0(默认值) 的话，那么不会进行去抖。</li>
<li><code>equals</code>: 默认值是 <code>comparer.default</code> 。如果指定的话，这个比较器函数被用来比较由 <em>数据</em> 函数产生的前一个值和后一个值。只有比较器函数返回 false <em>效果</em> 函数才会被调用。此选项如果指定的话，会覆盖 <code>compareStructural</code> 选项。</li>
<li><code>name</code>: 字符串，用于在例如像 <a href="https://cn.mobx.js.org/refguide/spy.html" target="_blank" rel="noopener"><code>spy</code></a> 这样事件中用作此 reaction 的名称。</li>
<li><code>onError</code>: 用来处理 reaction 的错误，而不是传播它们。</li>
<li><code>scheduler</code>: 设置自定义调度器以决定如何调度 autorun 函数的重新运行</li>
</ul>
<p><strong>示例</strong>：</p>
<p>在下面的示例中，<code>reaction1</code>、<code>reaction2</code> 和 <code>autorun1</code> 都会对 <code>todos</code> 数组中的 todo 的添加、删除或替换作出反应。 但只有 <code>reaction2</code> 和 <code>autorun</code> 会对某个 todo 的 <code>title</code> 变化作出反应，因为在 <code>reaction2</code> 的数据表达式中使用了 <code>title</code>，而 <code>reaction1</code> 的数据表达式没有使用。 <code>autorun</code> 追踪完整的副作用，因此它将始终正确触发，但也更容易意外地访问相关数据。 还可参见 <a href="https://cn.mobx.js.org/best/react" target="_blank" rel="noopener">MobX 会对什么作出反应?</a>.</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> todos = observable([</span><br><span class="line">    &#123;</span><br><span class="line">        title: <span class="string">"Make coffee"</span>,</span><br><span class="line">        done: <span class="literal">true</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">        title: <span class="string">"Find biscuit"</span>,</span><br><span class="line">        done: <span class="literal">false</span></span><br><span class="line">    &#125;</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// reaction 的错误用法: 对 length 的变化作出反应, 而不是 title 的变化!</span></span><br><span class="line"><span class="keyword">const</span> reaction1 = reaction(</span><br><span class="line">    () =&gt; todos.length,</span><br><span class="line">    length =&gt; <span class="built_in">console</span>.log(<span class="string">"reaction 1:"</span>, todos.map(<span class="function"><span class="params">todo</span> =&gt;</span> todo.title).join(<span class="string">", "</span>))</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// reaction 的正确用法: 对 length 和 title 的变化作出反应</span></span><br><span class="line"><span class="keyword">const</span> reaction2 = reaction(</span><br><span class="line">    () =&gt; todos.map(<span class="function"><span class="params">todo</span> =&gt;</span> todo.title),</span><br><span class="line">    titles =&gt; <span class="built_in">console</span>.log(<span class="string">"reaction 2:"</span>, titles.join(<span class="string">", "</span>))</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// autorun 对它函数中使用的任何东西作出反应</span></span><br><span class="line"><span class="keyword">const</span> autorun1 = autorun(</span><br><span class="line">    () =&gt; <span class="built_in">console</span>.log(<span class="string">"autorun 1:"</span>, todos.map(<span class="function"><span class="params">todo</span> =&gt;</span> todo.title).join(<span class="string">", "</span>))</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">todos.push(&#123; <span class="attr">title</span>: <span class="string">"explain reactions"</span>, <span class="attr">done</span>: <span class="literal">false</span> &#125;);</span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// reaction 1: Make coffee, find biscuit, explain reactions</span></span><br><span class="line"><span class="comment">// reaction 2: Make coffee, find biscuit, explain reactions</span></span><br><span class="line"><span class="comment">// autorun 1: Make coffee, find biscuit, explain reactions</span></span><br><span class="line"></span><br><span class="line">todos[<span class="number">0</span>].title = <span class="string">"Make tea"</span></span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// reaction 2: Make tea, find biscuit, explain reactions</span></span><br><span class="line"><span class="comment">// autorun 1: Make tea, find biscuit, explain reactions</span></span><br></pre></td></tr></table></figure>

<p>在下面的示例中，<code>reaction3</code> 会对 <code>counter</code> 中的 count 作出反应。 当调用 <code>reaction</code> 时，第二个参数会作为清理函数使用。 下面的示例展示了 <code>reaction</code> 只会调用一次。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> counter = observable(&#123; <span class="attr">count</span>: <span class="number">0</span> &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只调用一次并清理掉 reaction : 对 observable 值作出反应。</span></span><br><span class="line"><span class="keyword">const</span> reaction3 = reaction(</span><br><span class="line">    () =&gt; counter.count,</span><br><span class="line">    (count, reaction) =&gt; &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"reaction 3: invoked. counter.count = "</span> + count);</span><br><span class="line">        reaction.dispose();</span><br><span class="line">    &#125;</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">counter.count = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// reaction 3: invoked. counter.count = 1</span></span><br><span class="line"></span><br><span class="line">counter.count = <span class="number">2</span>;</span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// (There are no logging, because of reaction disposed. But, counter continue reaction)</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(counter.count);</span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<p>粗略地讲，reaction 是 <code>computed(expression).observe(action(sideEffect))</code> 或 <code>autorun(() =&gt; action(sideEffect)(expression))</code> 的语法糖。</p>
<h2 id="observer"><a href="#observer" class="headerlink" title="@observer"></a>@observer</h2><p><code>observer</code> 函数/装饰器可以用来将 React 组件转变成响应式组件。 它用 <code>mobx.autorun</code> 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。 <code>observer</code> 是由单独的 <code>mobx-react</code> 包提供的。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observer&#125; <span class="keyword">from</span> <span class="string">"mobx-react"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> timerData = observable(&#123;</span><br><span class="line">    secondsPassed: <span class="number">0</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    timerData.secondsPassed++;</span><br><span class="line">&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">@observer <span class="class"><span class="keyword">class</span> <span class="title">Timer</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">    render() &#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123; this.props.timerData.secondsPassed &#125; <span class="tag">&lt;/<span class="name">span</span>&gt;</span></span> )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">Timer</span> <span class="attr">timerData</span>=<span class="string">&#123;timerData&#125;</span> /&gt;</span></span>, <span class="built_in">document</span>.body);</span><br></pre></td></tr></table></figure>

<p>小贴士: 当 <code>observer</code> 需要组合其它装饰器或高阶组件时，请确保 <code>observer</code> 是最深处(第一个应用)的装饰器，否则它可能什么都不做。</p>
<p>注意，使用 <code>@observer</code> 装饰器是可选的，它和 <code>observer(class Timer ... { })</code> 达到的效果是一样的。</p>
<p><strong>陷阱: 组件中的间接引用值</strong></p>
<p>MobX 可以做很多事，但是它无法使原始数据类型值转变成可观察的(尽管它可以用对象来包装它们，参见 <a href="https://cn.mobx.js.org/refguide/boxed.html" target="_blank" rel="noopener">boxed observables</a>)。 所以<strong>值</strong>是不可观察的，但是对象的<strong>属性</strong>可以。这意味着 <code>@observer</code> 实际上是对间接引用(dereference)值的反应。 那么在上面的示例中，如果是用下面这种方式初始化的，<code>Timer</code> 组件是<strong>不会</strong>有反应的:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">React.render(<span class="xml"><span class="tag">&lt;<span class="name">Timer</span> <span class="attr">timerData</span>=<span class="string">&#123;timerData.secondsPassed&#125;</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br></pre></td></tr></table></figure>

<p>在这个代码片段中只是把 <code>secondsPassed</code> 的当前值传递给了 <code>Timer</code> 组件，这个值是不可变值<code>0</code>(JS中所有的原始类型值都是不可变的)。 这个数值永远都不会改变，因此 <code>Timer</code> 组件不会更新。<code>secondsPassed</code>的值将来会发生改变， 所以我们需要在组件<strong>中</strong>访问它。或者换句话说: 值需要<strong>通过引用</strong>来传递而不是通过(字面量)值来传递。</p>
<p><strong>ES5 支持</strong></p>
<p>在ES5环境中，可以简单地使用 <code>observer(React.createClass({ ...</code> 来定义观察者组件。还可以参见<a href="https://cn.mobx.js.org/best/syntax.md" target="_blank" rel="noopener">语法指南</a>。</p>
<p><strong>无状态函数组件</strong></p>
<p>上面的 <code>Timer</code> 组件还可以通过使用 <code>observer</code> 传递的无状态函数组件来编写:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observer&#125; <span class="keyword">from</span> <span class="string">"mobx-react"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Timer = observer(<span class="function">(<span class="params">&#123; timerData &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;span&gt;Seconds passed: &#123; timerData.secondsPassed &#125; &lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">);</span></span><br></pre></td></tr></table></figure>

<p><strong>可观察的局部组件状态</strong></p>
<p>就像普通类一样，你可以通过使用 <code>@observable</code> 装饰器在React组件上引入可观察属性。 这意味着你可以在组件中拥有功能同样强大的本地状态(local state)，而不需要通过 React 的冗长和强制性的 <code>setState</code> 机制来管理。 响应式状态会被 <code>render</code> 提取调用，但不会调用其它 React 的生命周期方法，除了 <code>componentWillUpdate</code> 和 <code>componentDidUpdate</code> 。 如果你需要用到其他 React 生命周期方法 ，只需使用基于 <code>state</code> 的常规 React API 即可。</p>
<p>上面的例子还可以这样写:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observer&#125; <span class="keyword">from</span> <span class="string">"mobx-react"</span></span><br><span class="line"><span class="keyword">import</span> &#123;observable&#125; <span class="keyword">from</span> <span class="string">"mobx"</span></span><br><span class="line"></span><br><span class="line">@observer <span class="class"><span class="keyword">class</span> <span class="title">Timer</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">    @observable secondsPassed = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    componentWillMount() &#123;</span><br><span class="line">        setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">this</span>.secondsPassed++</span><br><span class="line">        &#125;, <span class="number">1000</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="xml"><span class="tag">&lt;<span class="name">span</span>&gt;</span>Seconds passed: &#123; this.secondsPassed &#125; <span class="tag">&lt;/<span class="name">span</span>&gt;</span></span> )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(<span class="xml"><span class="tag">&lt;<span class="name">Timer</span> /&gt;</span></span>, <span class="built_in">document</span>.body)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>对于使用可观察的局部组件状态更多的优势，请参见<a href="https://medium.com/@mweststrate/3-reasons-why-i-stopped-using-react-setstate-ab73fc67a42e" target="_blank" rel="noopener">为什么我不再使用 <code>setState</code> 的三个理由</a>。</p>
<h3 id="使用-inject-将组件连接到提供的-stores"><a href="#使用-inject-将组件连接到提供的-stores" class="headerlink" title="使用 inject 将组件连接到提供的 stores"></a>使用 <code>inject</code> 将组件连接到提供的 stores</h3><p><code>mobx-react</code> 包还提供了 <code>Provider</code> 组件，它使用了 React 的上下文(context)机制，可以用来向下传递 <code>stores</code>。 要连接到这些 stores，需要传递一个 stores 名称的列表给 <code>inject</code>，这使得 stores 可以作为组件的 <code>props</code> 使用。</p>
<p><em>注意: 从 mobx-react 4开始，注入 stores 的语法发生了变化，应该一直使用 <code>inject(stores)(component)</code> 或 <code>@inject(stores) class Component...</code>。 直接传递 store 名称给 <code>observer</code> 的方式已废弃。</em></p>
<p>示例代码如下：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> colors = observable(&#123;</span><br><span class="line">   foreground: <span class="string">'#000'</span>,</span><br><span class="line">   background: <span class="string">'#fff'</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> App = <span class="function"><span class="params">()</span> =&gt;</span></span><br><span class="line">  &lt;Provider colors=&#123;colors&#125;&gt;</span><br><span class="line">     &lt;app stuff... /&gt;</span><br><span class="line">  &lt;<span class="regexp">/Provider&gt;;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const Button = inject("colors")(observer((&#123; colors, label, onClick &#125;) =&gt;</span></span><br><span class="line"><span class="regexp">  &lt;button style=&#123;&#123;</span></span><br><span class="line"><span class="regexp">      color: colors.foreground,</span></span><br><span class="line"><span class="regexp">      backgroundColor: colors.background</span></span><br><span class="line"><span class="regexp">    &#125;&#125;</span></span><br><span class="line"><span class="regexp">    onClick=&#123;onClick&#125;</span></span><br><span class="line"><span class="regexp">  &gt;&#123;label&#125;&lt;/</span>button&gt;</span><br><span class="line">));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 稍后..</span></span><br><span class="line">colors.foreground = <span class="string">'blue'</span>;</span><br><span class="line"><span class="comment">// 所有button都会更新</span></span><br></pre></td></tr></table></figure>

<p>更多资料，请参见 <a href="https://github.com/mobxjs/mobx-react#provider-and-inject" target="_blank" rel="noopener"><code>mobx-react</code> 文档</a>。</p>
<p><strong>何时使用 <code>observer</code>?</strong></p>
<p>简单来说: <em>所有渲染 observable 数据的组件</em>。 如果你不想将组件标记为 observer，例如为了减少通用组件包的依赖，请确保只传递普通数据。</p>
<p>使用 <code>@observer</code> 的话，不再需要从渲染目的上来区分是“智能组件”还是“无脑”组件。 在组件的事件处理、发起请求等方面，它也是一个很好的分离关注点。 当所有组件它们<strong>自己的</strong>依赖项有变化时，组件自己会响应更新。 而它的计算开销是可以忽略的，并且它会确保不管何时，只要当你开始使用 observable 数据时，组件都将会响应它的变化。 更多详情，请参见 <a href="https://www.reddit.com/r/reactjs/comments/4vnxg5/free_eggheadio_course_learn_mobx_react_in_30/d61oh0l" target="_blank" rel="noopener">这里</a>。</p>
<p>**<code>observer</code> 和 <code>PureComponent</code>**：</p>
<p>如果传递给组件的数据是响应式的,<code>observer</code> 还可以防止当组件的 <em>props</em> 只是浅改变时的重新渲染，这是很有意义的。 这个行为与 <a href="https://reactjs.org/docs/react-api.html#reactpurecomponent" target="_blank" rel="noopener">React PureComponent</a> 相似，不同在于这里的 <em>state</em> 的更改仍然会被处理。 如果一个组件提供了它自己的 <code>shouldComponentUpdate</code>，这个方法会被优先调用。 想要更详细的解释，请参见这个 <a href="https://github.com/mobxjs/mobx/issues/101" target="_blank" rel="noopener">github issue</a>。</p>
<p><strong><code>componentWillReact</code> (生命周期钩子)</strong></p>
<p>React 组件通常在新的堆栈上渲染，这使得通常很难弄清楚是什么<strong>导致</strong>组件的重新渲染。 当使用 <code>mobx-react</code> 时可以定义一个新的生命周期钩子函数 <code>componentWillReact</code>(一语双关)。当组件因为它观察的数据发生了改变，它会安排重新渲染，这个时候 <code>componentWillReact</code> 会被触发。这使得它很容易追溯渲染并找到导致渲染的操作(action)。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;observer&#125; <span class="keyword">from</span> <span class="string">"mobx-react"</span>;</span><br><span class="line"></span><br><span class="line">@observer <span class="class"><span class="keyword">class</span> <span class="title">TodoView</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">    componentWillReact() &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"I will re-render, since the todo has changed!"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>this.props.todo.title<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<ul>
<li><code>componentWillReact</code> 不接收参数</li>
<li><code>componentWillReact</code> 初始化渲染前不会触发 (使用 <code>componentWillMount</code> 替代)</li>
<li><code>componentWillReact</code> 对于 mobx-react@4+, 当接收新的 props 时并在 <code>setState</code> 调用后会触发此钩子</li>
</ul>
<p><strong>MobX-React-DevTools</strong>：</p>
<p>结合 <code>@observer</code>，可以使用 MobX-React-DevTools ，它精确地显示了何时重新渲染组件，并且可以检查组件的数据依赖关系。 详情请参见 <a href="https://cn.mobx.js.org/best/devtools.html" target="_blank" rel="noopener">开发者工具</a> 。</p>
<h3 id="observer-组件特性"><a href="#observer-组件特性" class="headerlink" title="observer 组件特性"></a>observer 组件特性</h3><ul>
<li>Observer 仅订阅在上次渲染期间活跃使用的数据结构。这意味着你不会订阅不足(under-subscribe)或者过度订阅(over-subscribe)。你甚至可以在渲染方法中使用仅在未来时间段可用的数据。 这是异步加载数据的理想选择。</li>
<li>你不需要声明组件将使用什么数据。相反，依赖关系在运行时会确定并以非常细粒度的方式进行跟踪。</li>
<li>通常，响应式组件没有或很少有状态，因为在与其他组件共享的对象中封装(视图)状态通常更方便。但你仍然可以自由地使用状态。</li>
<li><code>@observer</code> 以和 <code>PureComponent</code> 同样的方式实现了 <code>shouldComponentUpdate</code>，因此子组件可以避免不必要的重新渲染。</li>
<li>响应式组件单方面加载数据，即使子组件要重新渲染，父组件也不会进行不必要地重新渲染。</li>
<li><code>@observer</code> 不依赖于 React 的上下文系统。</li>
<li>mobx-react@4+ 中，observer 组件的props 对象和 state 对象都会自动地转变为 observable，这使得创建 @computed 属性更容易，@computed 属性是根据组件内部的 props 推导得到的。如果在 <code>@observer</code> 组件中包含 reaction(例如 <code>autorun</code>) 的话，当 reaction 使用的特定属性不再改变时，reaction 是不会再重新运行的，在 reaction 中使用的特定 props 一定要间接引用(例如 <code>const myProp = props.myProp</code>)。不然，如果你在 reaction 中引用了 <code>props.myProp</code>，那么 props 的<strong>任何</strong>改变都会导致 reaction 的重新运行。对于 <strong>React-Router 的典型用例，请参见<a href="https://alexhisen.gitbooks.io/mobx-recipes/content/observable-based-routing.html" target="_blank" rel="noopener">这篇文章</a>。</strong></li>
</ul>
<p><strong>在编译器中启用装饰器</strong>：</p>
<p>在使用 TypeScript 或 Babel 这些等待ES标准定义的编：译器时，默认情况下是不支持装饰器的。</p>
<ul>
<li>对于 <em>typescript</em>，启用 <code>--experimentalDecorators</code> 编译器标识或者在 <code>tsconfig.json</code> 中把编译器属性 <code>experimentalDecorators</code> 设置为 <code>true</code> (推荐做法)</li>
<li>对于 <em>babel5</em>，确保把 <code>--stage 0</code> 传递给 Babel CLI</li>
<li>对于 <em>babel6</em>，参见此 <a href="https://github.com/mobxjs/mobx/issues/105" target="_blank" rel="noopener">issue</a> 中建议的示例配置。</li>
</ul>
<h1 id="MobX-会对什么作出反应"><a href="#MobX-会对什么作出反应" class="headerlink" title="MobX 会对什么作出反应?"></a>MobX 会对什么作出反应?</h1><p>MobX 通常会对你期望的东西做出反应。 这意味着在90％的场景下，mobx “都可以工作”。 然而，在某些时候，你会遇到一个情况，它可能不会像你所期望的那样工作。 在这个时候理解 MobX 如何确定对什么有反应就显得尤为重要。</p>
<blockquote>
<p>MobX 会对在<strong>追踪函数</strong>执行<strong>过程</strong>中<strong>读取</strong>现存的可观察属性做出反应。</p>
</blockquote>
<ul>
<li><strong>“读取”</strong> 是对象属性的间接引用，可以用过 <code>.</code> (例如 <code>user.name</code>) 或者 <code>[]</code> (例如 <code>user[&#39;name&#39;]</code>) 的形式完成。</li>
<li><strong>“追踪函数”</strong> 是 <code>computed</code> 表达式、observer 组件的 <code>render()</code> 方法和 <code>when</code>、<code>reaction</code> 和 <code>autorun</code> 的第一个入参函数。</li>
<li><strong>“过程(during)”</strong> 意味着只追踪那些在函数执行时被读取的 observable 。这些值是否由追踪函数直接或间接使用并不重要。</li>
</ul>
<p>换句话说，MobX 不会对其作出反应:</p>
<ul>
<li>从 observable 获取的值，但是在追踪函数之外</li>
<li>在异步调用的代码块中读取的 observable</li>
</ul>
<p><strong>MobX 追踪属性访问，而不是值</strong>：</p>
<p>用一个示例来阐述上述规则，假设你有如下的 observable 数据结构(默认情况下 <code>observable</code> 会递归应用，所以本示例中的所有字段都是可观察的)。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> message = observable(&#123;</span><br><span class="line">    title: <span class="string">"Foo"</span>,</span><br><span class="line">    author: &#123;</span><br><span class="line">        name: <span class="string">"Michel"</span></span><br><span class="line">    &#125;,</span><br><span class="line">    likes: [</span><br><span class="line">        <span class="string">"John"</span>, <span class="string">"Sara"</span></span><br><span class="line">    ]</span><br><span class="line">&#125;)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>在内存中看起来像下面这样。 绿色框表示<strong>可观察</strong>属性。 请注意，<strong>值</strong>本身是不可观察的！</p>
<p><img src="/../../image/Mobx%E8%BF%9B%E9%98%B6/observed-refs.png" alt="MobX reacts to changing references"></p>
<p>现在 MobX 基本上所做的是记录你在函数中使用的是哪个<strong>箭头</strong>。之后，只要这些箭头中的其中一个改变了(它们开始引用别的东西了)，它就会重新运行。</p>
<h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>来看下下面这些示例(基于上面定义的 <code>message</code> 变量):</p>
<p><strong>正确的: 在追踪函数内进行间接引用</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.title)</span><br><span class="line">&#125;)</span><br><span class="line">message.title = <span class="string">"Bar"</span></span><br></pre></td></tr></table></figure>

<p>这将如预期一样会作出反应，<code>.title</code> 属性会被 autorun 间接引用并且在之后发生了改变，所以这个改变是能检测到的。</p>
<p>你可以通过在追踪函数内调用 <a href="https://cn.mobx.js.org/reguide/trace" target="_blank" rel="noopener"><code>trace()</code></a> 方法来验证 MobX 在追踪什么。以上面的函数为例，输出结果如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> disposer = autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.title)</span><br><span class="line">    trace()</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出:</span></span><br><span class="line"><span class="comment">// [mobx.trace] 'Autorun@2' tracing enabled</span></span><br><span class="line"></span><br><span class="line">message.title = <span class="string">"Hello"</span></span><br><span class="line"><span class="comment">// [mobx.trace] 'Autorun@2' is invalidated due to a change in: 'ObservableObject@1.title'</span></span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>还可以通过使用指定工具来获取内部的依赖 (或观察者):</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">getDependencyTree(disposer) <span class="comment">// 输出与 disposer 耦合的 reaction 的依赖树</span></span><br><span class="line"><span class="comment">// &#123; name: 'Autorun@4',</span></span><br><span class="line"><span class="comment">//  dependencies: [ &#123; name: 'ObservableObject@1.title' &#125; ] &#125;</span></span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><strong>错误的: 改变了非 observable 的引用</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.title)</span><br><span class="line">&#125;)</span><br><span class="line">message = observable(&#123; <span class="attr">title</span>: <span class="string">"Bar"</span> &#125;)</span><br></pre></td></tr></table></figure>

<p>这将<strong>不会</strong>作出反应。<code>message</code> 被改变了，但它不是 observable，它只是一个<strong>引用</strong> observable 的变量，但是变量(引用)本身并不是可观察的。</p>
<p><strong>错误的: 在追踪函数外进行间接引用</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> title = message.title;</span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(title)</span><br><span class="line">&#125;)</span><br><span class="line">message.title = <span class="string">"Bar"</span></span><br></pre></td></tr></table></figure>

<p>这将<strong>不会</strong>作出反应。<code>message.title</code> 是在 <code>autorun</code> 外面进行的间接引用，在间接引用的时候 <code>title</code> 变量只是包含 <code>message.title</code> 的值(字符串 <code>Foo</code>)而已。 <code>title</code> 变量不是 observable，所以 <code>autorun</code> 永远不会作出反应。</p>
<p><strong>正确的: 在追踪函数内进行间接引用</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.author.name)</span><br><span class="line">&#125;)</span><br><span class="line">message.author.name = <span class="string">"Sara"</span>;</span><br><span class="line">message.author = &#123; <span class="attr">name</span>: <span class="string">"John"</span> &#125;;</span><br></pre></td></tr></table></figure>

<p>对于这两个变化都将作出反应。 <code>author</code> 和 <code>author.name</code> 都是通过 <code>.</code> 访问的，使得 MobX 可以追踪这些引用。</p>
<p><strong>错误的: 存储 observable 对象的本地引用而不对其追踪</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> author = message.author;</span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(author.name)</span><br><span class="line">&#125;)</span><br><span class="line">message.author.name = <span class="string">"Sara"</span>;</span><br><span class="line">message.author = &#123; <span class="attr">name</span>: <span class="string">"John"</span> &#125;;</span><br></pre></td></tr></table></figure>

<p>对于第一个改变将会作出反应，<code>message.author</code> 和 <code>author</code> 是同一个对象，而 <code>name</code> 属性在 autorun 中进行的间接引用。 但对于第二个改变将<strong>不会</strong>作出反应，<code>message.author</code> 的关系没有通过 <code>autorun</code> 追踪。Autorun 仍然使用的是“老的” <code>author</code>。</p>
<p><strong>常见陷阱: console.log</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> message = observable(&#123; <span class="attr">title</span>: <span class="string">"hello"</span> &#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不会触发重新运行</span></span><br><span class="line">message.title = <span class="string">"Hello world"</span></span><br></pre></td></tr></table></figure>

<p>在上面的示例中，更新 <code>message</code> 的 <code>title</code> 属性不会被打印出来，因为没有在 <code>autorun</code> <code>内使用。autorun</code> 只依赖于 <code>message</code>，它不是 observable，而是常量。换句话说，对于 MobX 而言，它没有使用 <code>title</code>，因此与 <code>autorun</code> 无关。</p>
<p>事实上 <code>console.log</code> 会打印出 <code>message</code> 的 <code>title</code>，这是让人费解的，<code>console.log</code> 是异步 API，它只会稍后对参数进行格式化，因此 <code>autorun</code> 不会追踪 <code>console.log</code> 访问的数据。所以，请确保始终传递不变数据 ( immutable data ) 或防御副本给 <code>console.log</code>。</p>
<p>下面是一些解决方案，它们会对 <code>message.title</code> 作出反应:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.title) <span class="comment">// 很显然， 使用了 `.title` observable</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(mobx.toJS(message)) <span class="comment">// toJS 创建了深克隆，从而读取消息</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(&#123;...message&#125;) <span class="comment">// 创建了浅克隆，在此过程中也使用了 `.title`</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">JSON</span>.stringify(message)) <span class="comment">// 读取整个结构</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p><strong>正确的: 在追踪函数内访问数组属性</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.likes.length);</span><br><span class="line">&#125;)</span><br><span class="line">message.likes.push(<span class="string">"Jennifer"</span>);</span><br></pre></td></tr></table></figure>

<p>这将如预期一样会作出反应。<code>.length</code> 指向一个属性。 注意这会对数组中的<strong>任何</strong>更改做出反应。 数组不追踪每个索引/属性(如 observable 对象和映射)，而是将其作为一个整体追踪。</p>
<p><strong>错误的: 在追踪函数内索引越界访问</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.likes[<span class="number">0</span>]);</span><br><span class="line">&#125;)</span><br><span class="line">message.likes.push(<span class="string">"Jennifer"</span>);</span><br></pre></td></tr></table></figure>

<p>使用上面的示例数据是会作出反应的，数组的索引计数作为属性访问，但前提条件<strong>必须</strong>是提供的索引小于数组长度。 MobX 不会追踪还不存在的索引或者对象属性(当使用 observable 映射(map)时除外)。 所以建议总是使用 <code>.length</code> 来检查保护基于数组索引的访问。</p>
<p><strong>正确的: 在追踪函数内访问数组方法</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.likes.join(<span class="string">", "</span>));</span><br><span class="line">&#125;)</span><br><span class="line">message.likes.push(<span class="string">"Jennifer"</span>);</span><br></pre></td></tr></table></figure>

<p>这将如预期一样会作出反应。所有不会改变数组的数组方法都会自动地追踪。</p>
<hr>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.likes.join(<span class="string">", "</span>));</span><br><span class="line">&#125;)</span><br><span class="line">message.likes[<span class="number">2</span>] = <span class="string">"Jennifer"</span>;</span><br></pre></td></tr></table></figure>

<p>这将如预期一样会作出反应。所有数组的索引分配都可以检测到，但前提条件<strong>必须</strong>是提供的索引小于数组长度。</p>
<p><strong>错误的: “使用” observable 但没有访问它的任何属性</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    message.likes;</span><br><span class="line">&#125;)</span><br><span class="line">message.likes.push(<span class="string">"Jennifer"</span>);</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>这将<strong>不会</strong>作出反应。只是因为 <code>likes</code> 数组本身并没有被 <code>autorun</code> 使用，只是引用了数组。 所以相比之下，<code>messages.likes = [&quot;Jennifer&quot;]</code> 是会作出反应的，表达式没有修改数组，而是修改了 <code>likes</code> 属性本身。</p>
<p><strong>使用对象的非 observable 属性</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.postDate)</span><br><span class="line">&#125;)</span><br><span class="line">message.postDate = <span class="keyword">new</span> <span class="built_in">Date</span>()</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><em>MobX 4</em></p>
<p>这将<strong>不会</strong>作出反应。MobX 只能追踪 observable 属性，上面的 <code>postDate</code> 还未被定义为 observable 属性。 但是，仍然可以使用 MobX 提供的 <code>get</code> 和 <code>set</code> 方法来使其工作:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">get</span>(message, "postDate"))</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">set</span>(message, "postDate",  new Date())</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><em>MobX 5</em></p>
<p>在 MobX 5 中是<strong>会</strong>作出反应的，因为 MobX 5 可以追踪还不存在的属性。 注意，这只适用于由 <code>observable</code> 或 <code>observable.object</code> 创建出的对象。 对于类实例上的新属性，还是无法自动将其变成 observable 的。</p>
<p><strong>[MobX 4 及以下版本] 错误的: 使用 observable 对象还不存在的属性</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(message.postDate)</span><br><span class="line">&#125;)</span><br><span class="line">extendObservable(message, &#123;</span><br><span class="line">    postDate: <span class="keyword">new</span> <span class="built_in">Date</span>()</span><br><span class="line">&#125;)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>这将<strong>不会</strong>作出反应。MobX 不会对当追踪开始时还不能存在的 observable 属性作出反应。 如果两个表达式交换下顺序，或者任何其它的 observable 引起 <code>autorun</code> 再次运行的话，<code>autorun</code> 也会开始追踪 <code>postDate</code> 属性了。</p>
<p><strong>正确的: 使用映射中还不存在的项</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> twitterUrls = observable.map(&#123;</span><br><span class="line">    <span class="string">"John"</span>: <span class="string">"twitter.com/johnny"</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(twitterUrls.get(<span class="string">"Sara"</span>))</span><br><span class="line">&#125;)</span><br><span class="line">twitterUrls.set(<span class="string">"Sara"</span>, <span class="string">"twitter.com/horsejs"</span>)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>这将<strong>会</strong>作出反应。Observable 映射支持观察还不存在的项。 注意这里最初会输出 <code>undefined</code>。 可以通过使用 <code>twitterUrls.has(&quot;Sara&quot;)</code> 来先检查该项是否存在。 所以对于动态键集合，总是使用 observable 映射。</p>
<p><strong>正确的: 使用 MobX 工具来读/写对象</strong></p>
<p>MobX 4 之后还可以将 observable 对象当做动态集合使用，如果使用 MobX API 来进行读/更新操作，那么 MobX 可以追踪属性的变化。下面的代码同样可以进行反应:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">get</span>, <span class="keyword">set</span>, observable &#125; from "mobx"</span><br><span class="line"></span><br><span class="line">const twitterUrls = observable.object(&#123;</span><br><span class="line">    <span class="string">"John"</span>: <span class="string">"twitter.com/johnny"</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">get</span>(twitterUrls, "Sara")) // <span class="keyword">get</span> 可以追踪还未创建的属性</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">set</span>(twitterUrls, &#123; <span class="string">"Sara"</span> : <span class="string">"twitter.com/horsejs"</span>&#125;)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>想了解更多，请参见 <a href="https://mobx.js.org/refguide/api.html#direct-observable-manipulation" target="_blank" rel="noopener">对象操作 API</a> 。</p>
<h2 id="MobX-只追踪同步地访问数据"><a href="#MobX-只追踪同步地访问数据" class="headerlink" title="MobX 只追踪同步地访问数据"></a>MobX 只追踪同步地访问数据</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">upperCaseAuthorName</span>(<span class="params">author</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> baseName = author.name;</span><br><span class="line">    <span class="keyword">return</span> baseName.toUpperCase();</span><br><span class="line">&#125;</span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(upperCaseAuthorName(message.author))</span><br><span class="line">&#125;)</span><br><span class="line">message.author.name = <span class="string">"Chesterton"</span></span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p>这将<strong>会</strong>作出反应。尽管 <code>author.name</code> 不是在 <code>autorun</code> 本身的代码块中进行直接引用的。 MobX 会追踪发生在 <code>upperCaseAuthorName</code> 函数里的间接引用，因为它是在 autorun 执行期间发生的。</p>
<hr>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    setTimeout(</span><br><span class="line">        () =&gt; <span class="built_in">console</span>.log(message.likes.join(<span class="string">", "</span>)),</span><br><span class="line">        <span class="number">10</span></span><br><span class="line">    )</span><br><span class="line">&#125;)</span><br><span class="line">message.likes.push(<span class="string">"Jennifer"</span>);</span><br></pre></td></tr></table></figure>

<p>这将<strong>不会</strong>作出反应。在 <code>autorun</code> 执行期间没有访问到任何 observable，而只在 <code>setTimeout</code> 执行期间访问了。 通常来说，这是相当明显的，很少会导致问题。</p>
<h2 id="MobX-只会为数据是直接通过-render-存取的-observer-组件进行数据追踪"><a href="#MobX-只会为数据是直接通过-render-存取的-observer-组件进行数据追踪" class="headerlink" title="MobX 只会为数据是直接通过 render 存取的 observer 组件进行数据追踪"></a>MobX 只会为数据是直接通过 <code>render</code> 存取的 <code>observer</code> 组件进行数据追踪</h2><p>一个使用 <code>observer</code> 的常见错误是它不会追踪语法上看起来像 <code>observer</code> 父组件的数据，但实际上是由不同的组件渲染的。当组件的 render 回调函数在第一个类中传递给另一个组件时，经常会发生这种情况。</p>
<p>看下面这个人造的示例:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> MyComponent = observer(<span class="function">(<span class="params">&#123; message &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;SomeContainer</span><br><span class="line">        title = &#123;() =&gt; <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;message.title&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>&#125;</span><br><span class="line">    /&gt;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">message.title = <span class="string">"Bar"</span></span><br></pre></td></tr></table></figure>

<p>起初看上去一切似乎都是没问题的，除了 <code>&lt;div&gt;</code> 实际上不是由 <code>MyComponent</code>(有追踪的渲染) 渲染的，而是 <code>SomeContainer</code>。 所以要确保 <code>SomeContainer</code> 的 title 可以正确对新的 <code>message.title</code> 作出反应，<code>SomeContainer</code> 应该也是一个 <code>observer</code>。</p>
<p>如果 <code>SomeContainer</code> 来源于外部库的话，这通常不在你的掌控之中。在这种场景下，你可以用自己的无状态 <code>observer</code> 组件来包裹 <code>div</code> 解决此问题，或通过利用 <code>&lt;Observer&gt;</code>组件:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> MyComponent = observer(<span class="function">(<span class="params">&#123; message &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;SomeContainer</span><br><span class="line">        title = &#123;() =&gt; <span class="xml"><span class="tag">&lt;<span class="name">TitleRenderer</span> <span class="attr">message</span>=<span class="string">&#123;message&#125;</span> /&gt;</span></span>&#125;</span><br><span class="line">    /&gt;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> TitleRenderer = observer(<span class="function">(<span class="params">&#123; message &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;div&gt;&#123;message.title&#125;&lt;<span class="regexp">/div&gt;&#125;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">message.title = "Bar"</span></span><br></pre></td></tr></table></figure>

<p>另外一种方法可以避免创建额外组件，它同样适用了 mobx-react 内置的 <code>Observer</code> 组件，它不接受参数，只需要单个的 render 函数作为子节点:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> MyComponent = <span class="function">(<span class="params">&#123; message &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;SomeContainer</span><br><span class="line">        title = &#123;() =&gt;</span><br><span class="line">            &lt;Observer&gt;</span><br><span class="line">                &#123;() =&gt; <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;message.title&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>&#125;</span><br><span class="line">            &lt;<span class="regexp">/Observer&gt;</span></span><br><span class="line"><span class="regexp">        &#125;</span></span><br><span class="line"><span class="regexp">    /</span>&gt;</span><br><span class="line"></span><br><span class="line">message.title = <span class="string">"Bar"</span></span><br></pre></td></tr></table></figure>

<h2 id="避免在本地字段中缓存-observable"><a href="#避免在本地字段中缓存-observable" class="headerlink" title="避免在本地字段中缓存 observable"></a>避免在本地字段中缓存 observable</h2><p>一个常见的错误就是把间接引用的 observable 存储到本地变量，然后认为组件会作出反应。举例来说:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">@observer <span class="class"><span class="keyword">class</span> <span class="title">MyComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">component</span> </span>&#123;</span><br><span class="line">    author;</span><br><span class="line">    <span class="keyword">constructor</span>(props) &#123;</span><br><span class="line">        <span class="keyword">super</span>(props)</span><br><span class="line">        <span class="keyword">this</span>.author = props.message.author;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;this.author.name&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>组件会对 <code>author.name</code> 的变化作出反应，但不会对 <code>message</code> 本身的 <code>.author</code> 的变化作出反应！因为这个间接引用发生在 <code>render()</code> 之外，而<code>render()</code> 是 <code>observer</code> 组件的唯一追踪函数。 注意，即便把组件的 <code>author</code> 字段标记为 <code>@observable</code> 字段也不能解决这个问题，<code>author</code> 仍然是只分配一次。 这个问题可以简单地解决，方法是在 <code>render()</code> 中进行间接引用或者在组件实例上引入一个计算属性:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">@observer <span class="class"><span class="keyword">class</span> <span class="title">MyComponent</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">component</span> </span>&#123;</span><br><span class="line">    @computed <span class="keyword">get</span> author() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.props.message.author</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<h2 id="多个组件将如何渲染"><a href="#多个组件将如何渲染" class="headerlink" title="多个组件将如何渲染"></a>多个组件将如何渲染</h2><p>假设下面的组件是用来渲染上面的 <code>message</code> 对象的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Message = observer(<span class="function">(<span class="params">&#123; message &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;div&gt;</span><br><span class="line">        &#123;message.title&#125;</span><br><span class="line">        &lt;Author author=&#123; message.author &#125; /&gt;</span><br><span class="line">        &lt;Likes likes=&#123; message.likes &#125; /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const Author = observer((&#123; author &#125;) =&gt;</span></span><br><span class="line"><span class="regexp">    &lt;span&gt;&#123;author.name&#125;&lt;/</span>span&gt;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Likes = observer(<span class="function">(<span class="params">&#123; likes &#125;</span>) =&gt;</span></span><br><span class="line">    &lt;ul&gt;</span><br><span class="line">        &#123;likes.map(<span class="function"><span class="params">like</span> =&gt;</span></span><br><span class="line">            &lt;li&gt;&#123;like&#125;&lt;<span class="regexp">/li&gt;</span></span><br><span class="line"><span class="regexp">        )&#125;</span></span><br><span class="line"><span class="regexp">    &lt;/u</span>l&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>

<table>
<thead>
<tr>
<th>变化</th>
<th>重新渲染组件</th>
</tr>
</thead>
<tbody><tr>
<td><code>message.title = &quot;Bar&quot;</code></td>
<td><code>Message</code></td>
</tr>
<tr>
<td><code>message.author.name = &quot;Susan&quot;</code></td>
<td><code>Author</code> (<code>.author</code> 在 <code>Message</code> 中进行间接引用, 但没有改变)*</td>
</tr>
<tr>
<td><code>message.author = { name: &quot;Susan&quot;}</code></td>
<td><code>Message</code>, <code>Author</code></td>
</tr>
<tr>
<td><code>message.likes[0] = &quot;Michel&quot;</code></td>
<td><code>Likes</code></td>
</tr>
</tbody></table>
<p>注意:</p>
<ol>
<li>* 如果 <code>Author</code> 组件是像这样调用的: <code>&lt;Author author={ message.author.name} /&gt;</code> 。<code>Message</code> 会是进行间接引用的组件并对 <code>message.author.name</code> 的改变作出反应。尽管如此，<code>&lt;Author&gt;</code> 同样会重新渲染，因为它接收到了一个新的值。所以从性能上考虑，越晚进行间接引用越好。</li>
<li>** 如果 likes 数组里面的是对象而不是字符串，并且它们在它们自己的 <code>Like</code> 组件中渲染，那么对于发生在某个具体的 like 中发生的变化，<code>Likes</code> 组件将不会重新渲染。</li>
</ol>
<h1 id="改变-observables"><a href="#改变-observables" class="headerlink" title="改变 observables"></a>改变 observables</h1><h2 id="action"><a href="#action" class="headerlink" title="action"></a>action</h2><p>用法:</p>
<ul>
<li><code>action(fn)</code></li>
<li><code>action(name, fn)</code></li>
<li><code>@action classMethod() {}</code></li>
<li><code>@action(name) classMethod () {}</code></li>
<li><code>@action boundClassMethod = (args) =&gt; { body }</code></li>
<li><code>@action(name) boundClassMethod = (args) =&gt; { body }</code></li>
<li><code>@action.bound classMethod() {}</code></li>
</ul>
<p>任何应用都有动作。动作是任何用来修改状态的东西。 使用MobX你可以在代码中显式地标记出动作所在的位置。 动作可以有助于更好的组织代码。</p>
<p>它接收一个函数并返回具有同样签名的函数，但是用 <code>transaction</code>、<code>untracked</code> 和 <code>allowStateChanges</code> 包裹起来，尤其是 <code>transaction</code> 的自动应用会产生巨大的性能收益， 动作会分批处理变化并只在(最外层的)动作完成后通知计算值和反应。 这将确保在动作完成之前，在动作期间生成的中间值或未完成的值对应用的其余部分是不可见的。</p>
<p>建议对任何修改 observables 或具有副作用的函数使用 <code>(@)action</code> 。 结合开发者工具的话，动作还能提供非常有用的调试信息。</p>
<p>不支持使用 setters 的 <code>@action</code> 装饰器，但是，<a href="https://github.com/mobxjs/mobx/blob/gh-pages/docs/refguide/computed-decorator.md#setters-for-computed-values" target="_blank" rel="noopener">计算属性的 setters 是自动的动作</a>。</p>
<p>注意: 在将 MobX 配置为需要通过动作来更改状态时，必须使用 <code>action</code> ，参见 <a href="https://github.com/mobxjs/mobx/blob/gh-pages/docs/refguide/api.md#configure" target="_blank" rel="noopener"><code>enforceActions</code></a>。</p>
<p><strong>何时使用动作？</strong></p>
<p>应该永远只对<strong>修改</strong>状态的函数使用动作。 只执行查找，过滤器等函数<strong>不</strong>应该被标记为动作，以允许 MobX 跟踪它们的调用。</p>
<p><a href="https://github.com/mobxjs/mobx/blob/gh-pages/docs/refguide/api.md#configure" target="_blank" rel="noopener">“强制动作”</a> 强制所有状态变更都必须通过动作来完成。在大型、长期的项目中，这是十分有用的最佳实践。</p>
<p><strong>绑定的动作</strong></p>
<p><code>action</code> 装饰器/函数遵循 javascript 中标准的绑定规则。 但是，<code>action.bound</code> 可以用来自动地将动作绑定到目标对象。 注意，与 <code>action</code> 不同的是，<code>(@)action.bound</code> 不需要一个name参数，名称将始终基于动作绑定的属性。</p>
<p>示例:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Ticker</span> </span>&#123;</span><br><span class="line">    @observable tick = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    @action.bound</span><br><span class="line">    increment() &#123;</span><br><span class="line">        <span class="keyword">this</span>.tick++ <span class="comment">// 'this' 永远都是正确的</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ticker = <span class="keyword">new</span> Ticker()</span><br><span class="line">setInterval(ticker.increment, <span class="number">1000</span>)</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><em>注意: *action.bound* 不要和箭头函数一起使用；箭头函数已经是绑定过的并且不能重新绑定。</em></p>
<p><strong><code>runInAction(name?, thunk)</code></strong></p>
<p><code>runInAction</code> 是个简单的工具函数，它接收代码块并在(异步的)动作中执行。这对于</p>
<h2 id="async-action-amp-flows"><a href="#async-action-amp-flows" class="headerlink" title="async action &amp; flows"></a>async action &amp; flows</h2><h3 id="编写异步-Actions-动作"><a href="#编写异步-Actions-动作" class="headerlink" title="编写异步 Actions (动作)"></a>编写异步 Actions (动作)</h3><p><code>action</code> 包装/装饰器只会对当前运行的函数作出反应，而不会对当前运行函数所调用的函数（不包含在当前函数之内）作出反应！ 这意味着如果 action 中存在 <code>setTimeout</code>、promise 的 <code>then</code> 或 <code>async</code> 语句，并且在回调函数中某些状态改变了，那么这些回调函数也应该包装在 <code>action</code> 中。创建异步 action 有几种方式。不能说某种方式一定比其他的好，本章只是列出编写异步代码的几种不同方式而已。 我们先从一个基础的示例开始:</p>
<p><strong>Promises</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">mobx.configure(&#123; <span class="attr">enforceActions</span>: <span class="literal">true</span> &#125;) <span class="comment">// 不允许在动作之外进行状态修改</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    @observable githubProjects = []</span><br><span class="line">    @observable state = <span class="string">"pending"</span> <span class="comment">// "pending" / "done" / "error"</span></span><br><span class="line"></span><br><span class="line">    @action</span><br><span class="line">    fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        fetchGithubProjectsSomehow().then(</span><br><span class="line">            projects =&gt; &#123;</span><br><span class="line">                <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">                <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">            &#125;,</span><br><span class="line">            error =&gt; &#123;</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">            &#125;</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的示例会抛出异常，因为传给 <code>fetchGithubProjectsSomehow</code> promise 的回调函数不是 <code>fetchProjects</code> 动作的一部分，因为动作只会应用于当前栈。</p>
<p>首选的简单修复是将回调函数变成动作。(注意使用 <code>action.bound</code> 绑定在这很重要，以获取正确的 <code>this</code>!):</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">mobx.configure(&#123; <span class="attr">enforceActions</span>: <span class="literal">true</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    @observable githubProjects = []</span><br><span class="line">    @observable state = <span class="string">"pending"</span> <span class="comment">// "pending" / "done" / "error"</span></span><br><span class="line"></span><br><span class="line">    @action</span><br><span class="line">    fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        fetchGithubProjectsSomehow().then(<span class="keyword">this</span>.fetchProjectsSuccess, <span class="keyword">this</span>.fetchProjectsError)</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @action.bound</span><br><span class="line">    fetchProjectsSuccess(projects) &#123;</span><br><span class="line">        <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">    &#125;</span><br><span class="line">    @action.bound</span><br><span class="line">        fetchProjectsError(error) &#123;</span><br><span class="line">            <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>尽管这很整洁清楚，但异步流程复杂后可能会略显啰嗦。另外一种方案是你可以使用 <code>action</code> 关键字来包装 promises 回调函数。推荐这么做，但不是强制的，还需要给它们命名:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">mobx.configure(&#123; <span class="attr">enforceActions</span>: <span class="literal">true</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    @observable githubProjects = []</span><br><span class="line">    @observable state = <span class="string">"pending"</span> <span class="comment">// "pending" / "done" / "error"</span></span><br><span class="line"></span><br><span class="line">    @action</span><br><span class="line">    fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        fetchGithubProjectsSomehow().then(</span><br><span class="line">            <span class="comment">// 内联创建的动作</span></span><br><span class="line">            action(<span class="string">"fetchSuccess"</span>, projects =&gt; &#123;</span><br><span class="line">                <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">                <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">            &#125;),</span><br><span class="line">            <span class="comment">// 内联创建的动作</span></span><br><span class="line">            action(<span class="string">"fetchError"</span>, error =&gt; &#123;</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">            &#125;)</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong><code>runInAction</code> 工具函数</strong></p>
<p>内联动作的缺点是 TypeScript 无法对其进行类型推导，所以你应该为所有的回调函数定义类型。 你还可以只在动作中运行回调函数中状态修改的部分，而不是为整个回调创建一个动作。 这种模式的优势是它鼓励你不要到处写 <code>action</code>，而是在整个过程结束时尽可能多地对所有状态进行修改：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">mobx.configure(&#123; <span class="attr">enforceActions</span>: <span class="literal">true</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    @observable githubProjects = []</span><br><span class="line">    @observable state = <span class="string">"pending"</span> <span class="comment">// "pending" / "done" / "error"</span></span><br><span class="line"></span><br><span class="line">    @action</span><br><span class="line">    fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        fetchGithubProjectsSomehow().then(</span><br><span class="line">            projects =&gt; &#123;</span><br><span class="line">                <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">                <span class="comment">// 将‘“最终的”修改放入一个异步动作中</span></span><br><span class="line">                runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                    <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">                    <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">                &#125;)</span><br><span class="line">            &#125;,</span><br><span class="line">            error =&gt; &#123;</span><br><span class="line">                <span class="comment">// 过程的另一个结局:...</span></span><br><span class="line">                runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                    <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">                &#125;)</span><br><span class="line">            &#125;</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意，<code>runInAction</code> 还可以给定第一个参数作为名称。<code>runInAction(f)</code> 实际上是 <code>action(f)()</code> 的语法糖。</p>
<p><strong>async / await</strong></p>
<p>基于 async / await 的函数当开始使用动作时起初似乎会令人感到困惑。 因为在词法上它们看起来是同步函数，它给人的印象是 <code>@action</code> 应用于整个函数。 但事实并非若此，因为 async / await 只是围绕基于 promise 过程的语法糖。 结果是 <code>@action</code> 仅应用于代码块，直到第一个 <code>await</code> 。 在每个 <code>await</code> 之后，一个新的异步函数将启动，所以在每个 <code>await</code> 之后，状态修改代码应该被包装成动作。 这正是 <code>runInAction</code> 再次派上用场的地方:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">mobx.configure(&#123; <span class="attr">enforceActions</span>: <span class="literal">true</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    @observable githubProjects = []</span><br><span class="line">    @observable state = <span class="string">"pending"</span> <span class="comment">// "pending" / "done" / "error"</span></span><br><span class="line"></span><br><span class="line">    @action</span><br><span class="line">    <span class="keyword">async</span> fetchProjects() &#123;</span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> projects = <span class="keyword">await</span> fetchGithubProjectsSomehow()</span><br><span class="line">            <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">            <span class="comment">// await 之后，再次修改状态需要动作:</span></span><br><span class="line">            runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">                <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">            runInAction(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">Copy</span><br></pre></td></tr></table></figure>

<p><strong>flows</strong></p>
<p>然而，更好的方式是使用 <code>flow</code> 的内置概念。它们使用生成器。一开始可能看起来很不适应，但它的工作原理与 <code>async</code> / <code>await</code> 是一样的。只是使用 <code>function *</code> 来代替 <code>async</code>，使用 <code>yield</code> 代替 <code>await</code> 。 使用 <code>flow</code> 的优点是它在语法上基本与 <code>async</code> / <code>await</code> 是相同的 (只是关键字不同)，并且不需要手动用 <code>@action</code> 来包装异步代码，这样代码更简洁。</p>
<p><code>flow</code> 只能作为函数使用，不能作为装饰器使用。 <code>flow</code> 可以很好的与 MobX 开发者工具集成，所以很容易追踪 <code>async</code> 函数的过程。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">mobx.configure(&#123; <span class="attr">enforceActions</span>: <span class="literal">true</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    @observable githubProjects = []</span><br><span class="line">    @observable state = <span class="string">"pending"</span></span><br><span class="line"></span><br><span class="line">    fetchProjects = flow(<span class="function"><span class="keyword">function</span> * (<span class="params"></span>) </span>&#123; <span class="comment">// &lt;- 注意*号，这是生成器函数！</span></span><br><span class="line">        <span class="keyword">this</span>.githubProjects = []</span><br><span class="line">        <span class="keyword">this</span>.state = <span class="string">"pending"</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> projects = <span class="keyword">yield</span> fetchGithubProjectsSomehow() <span class="comment">// 用 yield 代替 await</span></span><br><span class="line">            <span class="keyword">const</span> filteredProjects = somePreprocessing(projects)</span><br><span class="line">            <span class="comment">// 异步代码块会被自动包装成动作并修改状态</span></span><br><span class="line">            <span class="keyword">this</span>.state = <span class="string">"done"</span></span><br><span class="line">            <span class="keyword">this</span>.githubProjects = filteredProjects</span><br><span class="line">        &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">            <span class="keyword">this</span>.state = <span class="string">"error"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="Object-Api"><a href="#Object-Api" class="headerlink" title="Object Api"></a>Object Api</h2><p><strong>直接操控 Observable</strong></p>
<p>现在有一个统一的工具 API 可以操控 observable 映射、对象和数组。这些 API 都是响应式的，这意味着如果使用 <code>set</code> 进行添加，使用 <code>values</code> 或 <code>keys</code> 进行迭代，即便是新属性的声明都可以被 MobX 检测到。</p>
<ul>
<li><strong><code>values(thing)</code></strong> 将集合中的所有值作为数组返回</li>
<li><strong><code>keys(thing)</code></strong> 将集合中的所有键作为数组返回</li>
<li><strong><code>entries(thing)</code></strong> 返回集合中的所有项的键值对数组</li>
</ul>
<p>使用 MobX 5 时, 实际上并不需要以下方法，但它们可以用来在 MobX 4 中来实现类似于 MobX 5 的行为:</p>
<ul>
<li><strong><code>set(thing, key, value)</code></strong> 或 <strong><code>set(thing, { key: value })</code></strong> 使用提供的键值对来更新给定的集合</li>
<li><strong><code>remove(thing, key)</code></strong> 从集合中移除指定的项。用于数组拼接</li>
<li><strong><code>has(thing, key)</code></strong> 如果集合中存在指定的 <em>observable</em> 属性就返回 true</li>
<li><strong><code>get(thing, key)</code></strong> 返回指定键下的子项</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="keyword">get</span>, <span class="keyword">set</span>, observable, values &#125; from "mobx"</span><br><span class="line"></span><br><span class="line">const twitterUrls = observable.object(&#123;</span><br><span class="line">    <span class="string">"John"</span>: <span class="string">"twitter.com/johnny"</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">get</span>(twitterUrls, "Sara")) // <span class="keyword">get</span> 可以追踪尚未存在的属性</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">autorun(() =&gt; &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">"All urls: "</span> + values(twitterUrls).join(<span class="string">", "</span>))</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span>(twitterUrls, &#123; <span class="string">"Sara"</span> : <span class="string">"twitter.com/horsejs"</span>&#125;)</span><br></pre></td></tr></table></figure>

    </div>
    
    
    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
        <div class="reward-container">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/images/alipay.png" alt="Li Yudong 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Li Yudong
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://example.com/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx%E8%BF%9B%E9%98%B6/" title="Mobx进阶">http://example.com/2021/11/30/盒马技术栈初学/Mobx进阶/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

    
      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/%E5%85%AC%E5%8F%B8%E6%8A%80%E6%9C%AF%E6%A0%88/" rel="tag"># 公司技术栈</a>
          </div>
    
        

    
        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/11/26/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Rax%E5%B0%8F%E7%A8%8B%E5%BA%8F%E8%BF%9B%E9%98%B6/" rel="prev" title="Rax小程序进阶">
      <i class="fa fa-chevron-left"></i> Rax小程序进阶
    </a></div>
      <div class="post-nav-item">
    <a href="/2021/11/30/%E7%9B%92%E9%A9%AC%E6%8A%80%E6%9C%AF%E6%A0%88%E5%88%9D%E5%AD%A6/Mobx-React/" rel="next" title="Mobx-React">
      Mobx-React <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#observable相关"><span class="nav-number">1.</span> <span class="nav-text">observable相关</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#observable"><span class="nav-number">1.1.</span> <span class="nav-text">observable</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#observable-1"><span class="nav-number">1.2.</span> <span class="nav-text">@observable</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Observable-对象"><span class="nav-number">1.3.</span> <span class="nav-text">Observable 对象</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Observable-数组"><span class="nav-number">1.4.</span> <span class="nav-text">Observable 数组</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Observable-映射"><span class="nav-number">1.5.</span> <span class="nav-text">Observable 映射</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#原值类型值和引用类型值"><span class="nav-number">1.6.</span> <span class="nav-text">原值类型值和引用类型值</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#装饰器"><span class="nav-number">1.7.</span> <span class="nav-text">装饰器</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#对-observables-作出响应"><span class="nav-number">2.</span> <span class="nav-text">对 observables 作出响应</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#computed"><span class="nav-number">2.1.</span> <span class="nav-text">(@)computed</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Autorun"><span class="nav-number">2.2.</span> <span class="nav-text">Autorun</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#when"><span class="nav-number">2.3.</span> <span class="nav-text">when</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Reaction"><span class="nav-number">2.4.</span> <span class="nav-text">Reaction</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#observer"><span class="nav-number">2.5.</span> <span class="nav-text">@observer</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#使用-inject-将组件连接到提供的-stores"><span class="nav-number">2.5.1.</span> <span class="nav-text">使用 inject 将组件连接到提供的 stores</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#observer-组件特性"><span class="nav-number">2.5.2.</span> <span class="nav-text">observer 组件特性</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#MobX-会对什么作出反应"><span class="nav-number">3.</span> <span class="nav-text">MobX 会对什么作出反应?</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#示例"><span class="nav-number">3.1.</span> <span class="nav-text">示例</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MobX-只追踪同步地访问数据"><span class="nav-number">3.2.</span> <span class="nav-text">MobX 只追踪同步地访问数据</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MobX-只会为数据是直接通过-render-存取的-observer-组件进行数据追踪"><span class="nav-number">3.3.</span> <span class="nav-text">MobX 只会为数据是直接通过 render 存取的 observer 组件进行数据追踪</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#避免在本地字段中缓存-observable"><span class="nav-number">3.4.</span> <span class="nav-text">避免在本地字段中缓存 observable</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#多个组件将如何渲染"><span class="nav-number">3.5.</span> <span class="nav-text">多个组件将如何渲染</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#改变-observables"><span class="nav-number">4.</span> <span class="nav-text">改变 observables</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#action"><span class="nav-number">4.1.</span> <span class="nav-text">action</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#async-action-amp-flows"><span class="nav-number">4.2.</span> <span class="nav-text">async action &amp; flows</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#编写异步-Actions-动作"><span class="nav-number">4.2.1.</span> <span class="nav-text">编写异步 Actions (动作)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Object-Api"><span class="nav-number">4.3.</span> <span class="nav-text">Object Api</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Li Yudong"
      src="/images/author.jpg">
  <p class="site-author-name" itemprop="name">Li Yudong</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">95</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/tiarmor1" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tiarmor1" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1157019137@qq.com" title="E-Mail → mailto:1157019137@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 2020 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Li Yudong</span>
</div>

        








      </div>
    </footer>
  </div>

  
  
  <script color='0,0,0' opacity='0.5' zIndex='-1' count='150' src="/lib1/canvas-nest/canvas-nest.min.js"></script>
  <script src="/lib1/anime.min.js"></script>
  <script src="/lib1/velocity/velocity.min.js"></script>
  <script src="/lib1/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

  
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/fireworks.js"></script>
  
</body>
</html>

